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 VikTrack *current_track; // ATM shared between new tracks and new routes
184 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
185 gboolean draw_sync_done;
186 gboolean draw_sync_do;
188 VikCoordMode coord_mode;
190 /* wp editing tool */
191 VikWaypoint *current_wp;
192 gpointer current_wp_id;
194 gboolean waypoint_rightclick;
196 /* track editing tool */
198 VikTrack *current_tp_track;
199 gpointer current_tp_id;
200 VikTrwLayerTpwin *tpwin;
202 /* track editing tool -- more specifically, moving tps */
205 /* route finder tool */
206 gboolean route_finder_started;
207 gboolean route_finder_check_added_track;
208 VikTrack *route_finder_added_track;
209 gboolean route_finder_append;
216 guint16 image_cache_size;
218 /* for waypoint text */
219 PangoLayout *wplabellayout;
221 gboolean has_verified_thumbnails;
223 GtkMenu *wp_right_click_menu;
224 GtkMenu *track_right_click_menu;
227 VikStdLayerMenuItem menu_selection;
229 gint highest_wp_number;
232 GtkWidget *tracks_analysis_dialog;
235 /* A caached waypoint image. */
238 gchar *image; /* filename */
241 struct DrawingParams {
246 guint16 width, height;
247 gdouble cc; // Cosine factor in track directions
248 gdouble ss; // Sine factor in track directions
249 const VikCoord *center;
250 gboolean one_zone, lat_lon;
251 gdouble ce1, ce2, cn1, cn2;
256 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
261 MA_SUBTYPE, // OR END for Layer only
270 typedef gpointer menu_array_layer[2];
271 typedef gpointer menu_array_sublayer[MA_LAST];
273 static void trw_layer_delete_item ( menu_array_sublayer values );
274 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
275 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
277 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
278 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
280 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
281 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
283 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
284 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
286 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
287 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
288 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
289 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
290 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
291 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
292 static void trw_layer_goto_track_center ( menu_array_sublayer values );
293 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
294 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
295 static void trw_layer_merge_with_other ( menu_array_sublayer values );
296 static void trw_layer_append_track ( menu_array_sublayer values );
297 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
298 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
299 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
300 static void trw_layer_split_segments ( menu_array_sublayer values );
301 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
302 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
303 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
304 static void trw_layer_reverse ( menu_array_sublayer values );
305 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
306 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
307 static void trw_layer_show_picture ( menu_array_sublayer values );
308 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
310 static void trw_layer_centerize ( menu_array_layer values );
311 static void trw_layer_auto_view ( menu_array_layer values );
312 static void trw_layer_goto_wp ( menu_array_layer values );
313 static void trw_layer_new_wp ( menu_array_layer values );
314 static void trw_layer_new_track ( menu_array_layer values );
315 static void trw_layer_new_route ( menu_array_layer values );
316 static void trw_layer_finish_track ( menu_array_layer values );
317 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
318 static void trw_layer_auto_tracks_view ( menu_array_layer values );
319 static void trw_layer_delete_all_tracks ( menu_array_layer values );
320 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
321 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
322 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
323 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
324 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
325 #ifdef VIK_CONFIG_GEOTAG
326 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
327 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
328 static void trw_layer_geotagging_track ( menu_array_sublayer values );
329 static void trw_layer_geotagging ( menu_array_layer values );
331 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
332 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
333 static void trw_layer_acquire_url_cb ( menu_array_layer values );
334 #ifdef VIK_CONFIG_OPENSTREETMAP
335 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
336 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
338 #ifdef VIK_CONFIG_GEOCACHES
339 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
341 #ifdef VIK_CONFIG_GEOTAG
342 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
344 static void trw_layer_acquire_file_cb ( menu_array_layer values );
345 static void trw_layer_gps_upload ( menu_array_layer values );
347 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
348 static void trw_layer_track_list_dialog ( menu_array_layer values );
349 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
351 // Specific route versions:
352 // Most track handling functions can handle operating on the route list
353 // However these ones are easier in separate functions
354 static void trw_layer_auto_routes_view ( menu_array_layer values );
355 static void trw_layer_delete_all_routes ( menu_array_layer values );
356 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
359 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
360 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
361 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
362 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
364 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
365 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
367 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
368 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
369 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
370 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
372 static void trw_layer_sort_all ( VikTrwLayer *vtl );
374 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
375 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
376 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
377 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
378 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
379 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
380 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
381 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
382 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
383 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
384 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
385 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
386 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
387 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
388 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
389 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
391 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
392 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
393 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
394 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
395 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
396 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
397 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
399 static void cached_pixbuf_free ( CachedPixbuf *cp );
400 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
402 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
403 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
405 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
406 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
408 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
409 static void highest_wp_number_reset(VikTrwLayer *vtl);
410 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
411 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
413 // Note for the following tool GtkRadioActionEntry texts:
414 // the very first text value is an internal name not displayed anywhere
415 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
416 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
417 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
418 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
419 static VikToolInterface trw_layer_tools[] = {
420 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
421 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
422 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
424 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
426 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
427 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
428 (VikToolMouseFunc) tool_new_track_click,
429 (VikToolMouseMoveFunc) tool_new_track_move,
430 (VikToolMouseFunc) tool_new_track_release,
431 (VikToolKeyFunc) tool_new_track_key_press,
432 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
433 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
435 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
436 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
437 (VikToolMouseFunc) tool_new_route_click,
438 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
439 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
440 (VikToolKeyFunc) tool_new_track_key_press, // -/#
441 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
442 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
444 { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
445 (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL,
446 (VikToolMouseFunc) tool_extended_route_finder_click,
447 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
448 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
449 (VikToolKeyFunc) tool_extended_route_finder_key_press,
450 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
451 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
453 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
454 (VikToolConstructorFunc) tool_edit_waypoint_create,
455 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
457 (VikToolMouseFunc) tool_edit_waypoint_click,
458 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
459 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
461 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
463 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
464 (VikToolConstructorFunc) tool_edit_trackpoint_create,
465 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
467 (VikToolMouseFunc) tool_edit_trackpoint_click,
468 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
469 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
471 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
473 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
474 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
475 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
477 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
482 TOOL_CREATE_WAYPOINT=0,
487 TOOL_EDIT_TRACKPOINT,
492 /****** PARAMETERS ******/
494 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
495 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
497 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
498 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
500 #define MIN_POINT_SIZE 2
501 #define MAX_POINT_SIZE 10
503 #define MIN_ARROW_SIZE 3
504 #define MAX_ARROW_SIZE 20
506 static VikLayerParamScale params_scales[] = {
507 /* min max step digits */
508 { 1, 10, 1, 0 }, /* line_thickness */
509 { 0, 100, 1, 0 }, /* track draw speed factor */
510 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
511 /* 5 * step == how much to turn */
512 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
513 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
514 { 5, 500, 5, 0 }, // 5: image cache_size - " "
515 { 0, 8, 1, 0 }, // 6: Background line thickness
516 { 1, 64, 1, 0 }, /* wpsize */
517 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
518 { 1, 100, 1, 0 }, // 9: elevation factor
519 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
520 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
523 static gchar* params_font_sizes[] = {
524 N_("Extra Extra Small"),
530 N_("Extra Extra Large"),
533 // Needs to align with vik_layer_sort_order_t
534 static gchar* params_sort_order[] = {
536 N_("Name Ascending"),
537 N_("Name Descending"),
538 N_("Date Ascending"),
539 N_("Date Descending"),
543 static VikLayerParamData black_color_default ( void ) {
544 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
546 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
547 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
548 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
549 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
550 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
551 static VikLayerParamData trackbgcolor_default ( void ) {
552 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
554 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
555 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
556 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
558 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
559 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
560 static VikLayerParamData wptextcolor_default ( void ) {
561 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
563 static VikLayerParamData wpbgcolor_default ( void ) {
564 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
566 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
567 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
569 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
570 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
571 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
573 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
575 static VikLayerParamData string_default ( void )
577 VikLayerParamData data;
582 VikLayerParam trw_layer_params[] = {
583 { 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 },
584 { 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 },
585 { 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 },
587 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
588 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
589 { 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 },
590 { 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 },
591 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
592 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
593 { 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 },
594 { 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 },
595 { 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 },
596 { 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 },
597 { 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 },
598 { 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 },
599 { 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 },
600 { 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 },
601 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
602 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 },
603 { 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 },
605 { 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 },
606 { 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 },
607 { 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,
608 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
609 { 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 },
611 { 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 },
612 { 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 },
613 { 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 },
614 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
615 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
616 { 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 },
617 { 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 },
618 { 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 },
619 { 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 },
620 { 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 },
622 { 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 },
623 { 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 },
624 { 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 },
625 { 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 },
627 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
628 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
629 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
630 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
633 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
635 // Sublayer visibilities
683 *** 1) Add to trw_layer_params and enumeration
684 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
687 /****** END PARAMETERS ******/
689 /* Layer Interface function definitions */
690 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
691 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
692 static void trw_layer_post_read ( VikTrwLayer *vtl, VikViewport *vvp, gboolean from_file );
693 static void trw_layer_free ( VikTrwLayer *trwlayer );
694 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
695 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
696 static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl );
697 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
698 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
699 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
700 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
701 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
702 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
703 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
704 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
705 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
706 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
707 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
708 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
709 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
710 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
711 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
712 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
713 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
714 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
715 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
716 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
717 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
718 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t );
719 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
720 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
721 /* End Layer Interface function definitions */
723 VikLayerInterface vik_trw_layer_interface = {
730 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
734 params_groups, /* params_groups */
735 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
739 (VikLayerFuncCreate) trw_layer_create,
740 (VikLayerFuncRealize) trw_layer_realize,
741 (VikLayerFuncPostRead) trw_layer_post_read,
742 (VikLayerFuncFree) trw_layer_free,
744 (VikLayerFuncProperties) NULL,
745 (VikLayerFuncDraw) trw_layer_draw,
746 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
747 (VikLayerFuncGetTimestamp) trw_layer_get_timestamp,
749 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
750 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
752 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
753 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
755 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
756 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
757 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
758 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
759 (VikLayerFuncLayerSelected) trw_layer_selected,
761 (VikLayerFuncMarshall) trw_layer_marshall,
762 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
764 (VikLayerFuncSetParam) trw_layer_set_param,
765 (VikLayerFuncGetParam) trw_layer_get_param,
766 (VikLayerFuncChangeParam) trw_layer_change_param,
768 (VikLayerFuncReadFileData) a_gpspoint_read_file,
769 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
771 (VikLayerFuncDeleteItem) trw_layer_del_item,
772 (VikLayerFuncCutItem) trw_layer_cut_item,
773 (VikLayerFuncCopyItem) trw_layer_copy_item,
774 (VikLayerFuncPasteItem) trw_layer_paste_item,
775 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
777 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
779 (VikLayerFuncSelectClick) trw_layer_select_click,
780 (VikLayerFuncSelectMove) trw_layer_select_move,
781 (VikLayerFuncSelectRelease) trw_layer_select_release,
782 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
785 static gboolean have_diary_program = FALSE;
786 static gchar *diary_program = NULL;
787 #define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program"
789 static gboolean have_geojson_export = FALSE;
791 static gboolean have_astro_program = FALSE;
792 static gchar *astro_program = NULL;
793 #define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program"
795 // NB Only performed once per program run
796 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
798 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) {
800 //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" );
801 diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" );
803 diary_program = g_strdup ( "rednotebook" );
807 // User specified so assume it works
808 have_diary_program = TRUE;
811 if ( g_find_program_in_path( diary_program ) ) {
812 gchar *mystdout = NULL;
813 gchar *mystderr = NULL;
814 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
815 gchar *cmd = g_strconcat ( diary_program, " --version", NULL ); // "rednotebook --version"
816 if ( g_spawn_command_line_sync ( cmd, &mystdout, &mystderr, NULL, NULL ) ) {
817 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
819 g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
821 g_warning ("Diary: stderr: %s", mystderr );
823 gchar **tokens = NULL;
824 if ( mystdout && g_strcmp0(mystdout, "") )
825 tokens = g_strsplit(mystdout, " ", 0);
827 tokens = g_strsplit(mystderr, " ", 0);
831 gchar *token = tokens[num];
832 while ( token && num < 2 ) {
834 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
835 have_diary_program = TRUE;
841 g_strfreev ( tokens );
848 if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
849 have_geojson_export = TRUE;
853 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) {
855 //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" );
856 astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" );
858 astro_program = g_strdup ( "stellarium" );
862 // User specified so assume it works
863 have_astro_program = TRUE;
865 if ( g_find_program_in_path( astro_program ) ) {
866 have_astro_program = TRUE;
870 GType vik_trw_layer_get_type ()
872 static GType vtl_type = 0;
876 static const GTypeInfo vtl_info =
878 sizeof (VikTrwLayerClass),
879 NULL, /* base_init */
880 NULL, /* base_finalize */
881 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
882 NULL, /* class_finalize */
883 NULL, /* class_data */
884 sizeof (VikTrwLayer),
886 NULL /* instance init */
888 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
893 VikTRWMetadata *vik_trw_metadata_new()
895 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
898 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
903 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
905 return vtl->metadata;
908 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
911 vik_trw_metadata_free ( vtl->metadata );
912 vtl->metadata = metadata;
917 const gchar *date_str;
919 const VikWaypoint *wpt;
924 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
928 // Might be an easier way to compare dates rather than converting the strings all the time...
929 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
930 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
932 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
941 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
945 // Might be an easier way to compare dates rather than converting the strings all the time...
946 if ( wpt->has_timestamp ) {
947 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
949 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
959 * Find an item by date
961 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
965 df.date_str = date_str;
970 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
972 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
974 if ( select && df.found ) {
975 if ( do_tracks && df.trk ) {
976 struct LatLon maxmin[2] = { {0,0}, {0,0} };
977 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
978 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
979 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
982 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
983 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
985 vik_layer_emit_update ( VIK_LAYER(vtl) );
990 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
992 static menu_array_sublayer values;
998 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
1001 values[MA_VTL] = vtl;
1002 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
1003 values[MA_SUBLAYER_ID] = sublayer;
1004 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
1006 trw_layer_delete_item ( values );
1009 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
1011 static menu_array_sublayer values;
1017 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
1020 values[MA_VTL] = vtl;
1021 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
1022 values[MA_SUBLAYER_ID] = sublayer;
1023 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
1025 trw_layer_copy_item_cb(values);
1026 trw_layer_cut_item_cb(values);
1029 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
1031 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1032 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
1033 gpointer sublayer = values[MA_SUBLAYER_ID];
1034 guint8 *data = NULL;
1037 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
1041 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1042 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
1043 if ( wp && wp->name )
1046 name = NULL; // Broken :(
1048 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1049 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1050 if ( trk && trk->name )
1053 name = NULL; // Broken :(
1056 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1057 if ( trk && trk->name )
1060 name = NULL; // Broken :(
1063 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1064 subtype, len, name, data);
1068 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1070 trw_layer_copy_item_cb(values);
1071 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1072 trw_layer_delete_item(values);
1075 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1077 // Slightly cheating method, routing via the panels capability
1078 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1081 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1091 GByteArray *ba = g_byte_array_new ();
1093 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1094 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1095 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1096 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1098 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1101 g_byte_array_append ( ba, id, il );
1109 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1116 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1120 w = vik_waypoint_unmarshall ( item, len );
1121 // When copying - we'll create a new name based on the original
1122 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1123 vik_trw_layer_add_waypoint ( vtl, name, w );
1124 waypoint_convert (NULL, w, &vtl->coord_mode);
1127 trw_layer_calculate_bounds_waypoints ( vtl );
1129 // Consider if redraw necessary for the new item
1130 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1131 vik_layer_emit_update ( VIK_LAYER(vtl) );
1134 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1138 t = vik_track_unmarshall ( item, len );
1139 // When copying - we'll create a new name based on the original
1140 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1141 vik_trw_layer_add_track ( vtl, name, t );
1142 vik_track_convert (t, vtl->coord_mode);
1145 // Consider if redraw necessary for the new item
1146 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1147 vik_layer_emit_update ( VIK_LAYER(vtl) );
1150 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1154 t = vik_track_unmarshall ( item, len );
1155 // When copying - we'll create a new name based on the original
1156 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1157 vik_trw_layer_add_route ( vtl, name, t );
1158 vik_track_convert (t, vtl->coord_mode);
1161 // Consider if redraw necessary for the new item
1162 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1163 vik_layer_emit_update ( VIK_LAYER(vtl) );
1169 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1176 static void image_cache_free ( VikTrwLayer *vtl )
1178 g_list_foreach ( vtl->image_cache->head, (GFunc)cached_pixbuf_free, NULL );
1179 g_queue_free ( vtl->image_cache );
1182 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1186 case PARAM_TV: vtl->tracks_visible = data.b; break;
1187 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1188 case PARAM_RV: vtl->routes_visible = data.b; break;
1189 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1190 case PARAM_TLFONTSIZE:
1191 if ( data.u < FS_NUM_SIZES ) {
1192 vtl->track_font_size = data.u;
1193 g_free ( vtl->track_fsize_str );
1194 switch ( vtl->track_font_size ) {
1195 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1196 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1197 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1198 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1199 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1200 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1201 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1205 case PARAM_DM: vtl->drawmode = data.u; break;
1207 vtl->track_color = data.c;
1208 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1210 case PARAM_DP: vtl->drawpoints = data.b; break;
1212 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1213 vtl->drawpoints_size = data.u;
1215 case PARAM_DE: vtl->drawelevation = data.b; break;
1216 case PARAM_DS: vtl->drawstops = data.b; break;
1217 case PARAM_DL: vtl->drawlines = data.b; break;
1218 case PARAM_DD: vtl->drawdirections = data.b; break;
1220 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1221 vtl->drawdirections_size = data.u;
1223 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1224 vtl->stop_length = data.u;
1226 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1227 vtl->elevation_factor = data.u;
1229 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1231 vtl->line_thickness = data.u;
1232 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1235 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1237 vtl->bg_line_thickness = data.u;
1238 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1242 vtl->track_bg_color = data.c;
1243 if ( vtl->track_bg_gc )
1244 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1246 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1247 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1248 case PARAM_DLA: vtl->drawlabels = data.b; break;
1249 case PARAM_DI: vtl->drawimages = data.b; break;
1250 case PARAM_IS: if ( data.u != vtl->image_size )
1252 vtl->image_size = data.u;
1253 image_cache_free ( vtl );
1254 vtl->image_cache = g_queue_new ();
1257 case PARAM_IA: if ( data.u != vtl->image_alpha )
1259 vtl->image_alpha = data.u;
1260 image_cache_free ( vtl );
1261 vtl->image_cache = g_queue_new ();
1264 case PARAM_ICS: vtl->image_cache_size = data.u;
1265 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1266 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1269 vtl->waypoint_color = data.c;
1270 if ( vtl->waypoint_gc )
1271 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1274 vtl->waypoint_text_color = data.c;
1275 if ( vtl->waypoint_text_gc )
1276 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1279 vtl->waypoint_bg_color = data.c;
1280 if ( vtl->waypoint_bg_gc )
1281 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1284 vtl->wpbgand = data.b;
1285 if ( vtl->waypoint_bg_gc )
1286 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1288 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1289 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1290 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1291 case PARAM_WPFONTSIZE:
1292 if ( data.u < FS_NUM_SIZES ) {
1293 vtl->wp_font_size = data.u;
1294 g_free ( vtl->wp_fsize_str );
1295 switch ( vtl->wp_font_size ) {
1296 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1297 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1298 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1299 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1300 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1301 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1302 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1306 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1308 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1309 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1310 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1311 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1317 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1319 VikLayerParamData rv;
1322 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1323 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1324 case PARAM_RV: rv.b = vtl->routes_visible; break;
1325 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1326 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1327 case PARAM_DM: rv.u = vtl->drawmode; break;
1328 case PARAM_TC: rv.c = vtl->track_color; break;
1329 case PARAM_DP: rv.b = vtl->drawpoints; break;
1330 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1331 case PARAM_DE: rv.b = vtl->drawelevation; break;
1332 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1333 case PARAM_DS: rv.b = vtl->drawstops; break;
1334 case PARAM_SL: rv.u = vtl->stop_length; break;
1335 case PARAM_DL: rv.b = vtl->drawlines; break;
1336 case PARAM_DD: rv.b = vtl->drawdirections; break;
1337 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1338 case PARAM_LT: rv.u = vtl->line_thickness; break;
1339 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1340 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1341 case PARAM_DI: rv.b = vtl->drawimages; break;
1342 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1343 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1344 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1345 case PARAM_IS: rv.u = vtl->image_size; break;
1346 case PARAM_IA: rv.u = vtl->image_alpha; break;
1347 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1348 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1349 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1350 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1351 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1352 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1353 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1354 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1355 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1356 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1358 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1359 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1360 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1361 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1367 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1369 // This '-3' is to account for the first few parameters not in the properties
1370 const gint OFFSET = -3;
1372 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1373 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1376 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1377 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1378 GtkWidget **ww2 = values[UI_CHG_LABELS];
1379 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1380 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1381 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1382 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1383 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1384 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1385 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1386 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1387 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1388 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1389 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1390 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1393 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1396 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1397 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1398 GtkWidget **ww2 = values[UI_CHG_LABELS];
1399 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1400 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1401 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1402 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1403 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1404 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1405 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1406 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1407 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1408 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1409 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1410 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1411 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1412 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1413 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1414 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1417 // Alter sensitivity of all track colours according to the draw track mode.
1420 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1421 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1422 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1423 GtkWidget **ww2 = values[UI_CHG_LABELS];
1424 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1425 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1426 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1427 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1430 case PARAM_MDTIME: {
1431 // Force metadata->timestamp to be always read-only for now.
1432 GtkWidget **ww = values[UI_CHG_WIDGETS];
1433 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1434 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1436 // NB Since other track settings have been split across tabs,
1437 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1442 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1449 // Use byte arrays to store sublayer data
1450 // much like done elsewhere e.g. vik_layer_marshall_params()
1451 GByteArray *ba = g_byte_array_new ( );
1456 guint object_length;
1459 // the length of the item
1460 // the sublayer type of item
1461 // the the actual item
1462 #define tlm_append(object_pointer, size, type) \
1464 object_length = (size); \
1465 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1466 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1467 g_byte_array_append ( ba, (object_pointer), object_length );
1469 // Layer parameters first
1470 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1471 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1472 g_byte_array_append ( ba, pd, pl );
1475 // Now sublayer data
1476 GHashTableIter iter;
1477 gpointer key, value;
1480 g_hash_table_iter_init ( &iter, vtl->waypoints );
1481 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1482 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1483 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1488 g_hash_table_iter_init ( &iter, vtl->tracks );
1489 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1490 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1491 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1496 g_hash_table_iter_init ( &iter, vtl->routes );
1497 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1498 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1499 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1509 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1511 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1513 gint consumed_length;
1515 // First the overall layer parameters
1516 memcpy(&pl, data, sizeof(pl));
1518 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1521 consumed_length = pl;
1522 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1524 #define tlm_size (*(gint *)data)
1525 // See marshalling above for order of how this is written
1527 data += sizeof_len_and_subtype + tlm_size;
1529 // Now the individual sublayers:
1531 while ( *data && consumed_length < len ) {
1532 // Normally four extra bytes at the end of the datastream
1533 // (since it's a GByteArray and that's where it's length is stored)
1534 // So only attempt read when there's an actual block of sublayer data
1535 if ( consumed_length + tlm_size < len ) {
1537 // Reuse pl to read the subtype from the data stream
1538 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1540 // Also remember to (attempt to) convert each coordinate in case this is pasted into a different drawmode
1541 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1542 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1543 gchar *name = g_strdup ( trk->name );
1544 vik_trw_layer_add_track ( vtl, name, trk );
1546 vik_track_convert (trk, vtl->coord_mode);
1548 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1549 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1550 gchar *name = g_strdup ( wp->name );
1551 vik_trw_layer_add_waypoint ( vtl, name, wp );
1553 waypoint_convert (NULL, wp, &vtl->coord_mode);
1555 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1556 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1557 gchar *name = g_strdup ( trk->name );
1558 vik_trw_layer_add_route ( vtl, name, trk );
1560 vik_track_convert (trk, vtl->coord_mode);
1563 consumed_length += tlm_size + sizeof_len_and_subtype;
1566 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1568 // Not stored anywhere else so need to regenerate
1569 trw_layer_calculate_bounds_waypoints ( vtl );
1574 // Keep interesting hash function at least visible
1576 static guint strcase_hash(gconstpointer v)
1578 // 31 bit hash function
1581 gchar s[128]; // malloc is too slow for reading big files
1584 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1585 p[i] = toupper(t[i]);
1591 for (p += 1; *p != '\0'; p++)
1592 h = (h << 5) - h + *p;
1599 // Stick a 1 at the end of the function name to make it more unique
1600 // thus more easily searchable in a simple text editor
1601 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1603 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1604 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1606 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1607 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1609 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1610 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1611 // and with normal PC processing capabilities - it has negligibile performance impact
1612 // This also minimized the amount of rework - as the management of the hash tables already exists.
1614 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1615 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1616 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1618 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1619 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1620 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1621 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1622 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1623 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1625 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1627 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1629 // Param settings that are not available via the GUI
1630 // Force to on after processing params (which defaults them to off with a zero value)
1631 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1633 rv->metadata = vik_trw_metadata_new ();
1634 rv->draw_sync_done = TRUE;
1635 rv->draw_sync_do = TRUE;
1636 rv->coord_mode = VIK_COORD_LATLON;
1637 // Everything else is 0, FALSE or NULL
1643 static void trw_layer_free ( VikTrwLayer *trwlayer )
1645 g_hash_table_destroy(trwlayer->waypoints);
1646 g_hash_table_destroy(trwlayer->waypoints_iters);
1647 g_hash_table_destroy(trwlayer->tracks);
1648 g_hash_table_destroy(trwlayer->tracks_iters);
1649 g_hash_table_destroy(trwlayer->routes);
1650 g_hash_table_destroy(trwlayer->routes_iters);
1652 /* ODC: replace with GArray */
1653 trw_layer_free_track_gcs ( trwlayer );
1655 if ( trwlayer->wp_right_click_menu )
1656 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1658 if ( trwlayer->track_right_click_menu )
1659 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1661 if ( trwlayer->tracklabellayout != NULL)
1662 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1664 if ( trwlayer->wplabellayout != NULL)
1665 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1667 if ( trwlayer->waypoint_gc != NULL )
1668 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1670 if ( trwlayer->waypoint_text_gc != NULL )
1671 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1673 if ( trwlayer->waypoint_bg_gc != NULL )
1674 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1676 g_free ( trwlayer->wp_fsize_str );
1677 g_free ( trwlayer->track_fsize_str );
1679 if ( trwlayer->tpwin != NULL )
1680 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1682 if ( trwlayer->tracks_analysis_dialog != NULL )
1683 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1685 image_cache_free ( trwlayer );
1688 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1692 dp->highlight = highlight;
1693 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1694 dp->xmpp = vik_viewport_get_xmpp ( vp );
1695 dp->ympp = vik_viewport_get_ympp ( vp );
1696 dp->width = vik_viewport_get_width ( vp );
1697 dp->height = vik_viewport_get_height ( vp );
1698 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1699 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1701 dp->center = vik_viewport_get_center ( vp );
1702 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1703 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1708 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1709 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1710 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1712 dp->ce1 = dp->center->east_west-w2;
1713 dp->ce2 = dp->center->east_west+w2;
1714 dp->cn1 = dp->center->north_south-h2;
1715 dp->cn2 = dp->center->north_south+h2;
1716 } else if ( dp->lat_lon ) {
1717 VikCoord upperleft, bottomright;
1718 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1719 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1720 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1721 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1722 dp->ce1 = upperleft.east_west;
1723 dp->ce2 = bottomright.east_west;
1724 dp->cn1 = bottomright.north_south;
1725 dp->cn2 = upperleft.north_south;
1728 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1732 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1733 * Here a simple traffic like light colour system is used:
1734 * . slow points are red
1735 * . average is yellow
1736 * . fast points are green
1738 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1741 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1742 if ( average_speed > 0 ) {
1743 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1744 if ( rv < low_speed )
1745 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1746 else if ( rv > high_speed )
1747 return VIK_TRW_LAYER_TRACK_GC_FAST;
1749 return VIK_TRW_LAYER_TRACK_GC_AVER;
1752 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1755 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1757 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1758 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1759 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1760 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1764 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1766 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1768 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1769 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1771 // Fallback if parse failure
1772 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1774 g_free ( label_markup );
1776 gint label_x, label_y;
1778 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1780 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1781 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1785 * distance_in_preferred_units:
1786 * @dist: The source distance in standard SI Units (i.e. metres)
1788 * TODO: This is a generic function that could be moved into globals.c or utils.c
1790 * Probably best used if you have a only few conversions to perform.
1791 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1792 * since it will be doing the preference check on each call
1794 * Returns: The distance in the units as specified by the preferences
1796 static gdouble distance_in_preferred_units ( gdouble dist )
1799 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1800 switch (dist_units) {
1801 case VIK_UNITS_DISTANCE_MILES:
1802 mydist = VIK_METERS_TO_MILES(dist);
1804 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1805 mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
1807 // VIK_UNITS_DISTANCE_KILOMETRES:
1809 mydist = dist/1000.0;
1816 * trw_layer_draw_dist_labels:
1818 * Draw a few labels along a track at nicely seperated distances
1819 * This might slow things down if there's many tracks being displayed with this on.
1821 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1823 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1824 25.0, 40.0, 50.0, 75.0, 100.0,
1825 150.0, 200.0, 250.0, 500.0, 1000.0};
1827 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1829 // Convert to specified unit to find the friendly breakdown value
1830 dist = distance_in_preferred_units ( dist );
1834 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1835 if ( chunksd[i] > dist ) {
1837 dist = chunksd[index];
1842 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1844 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1845 gdouble dist_i = dist * i;
1847 // Convert distance back into metres for use in finding a trackpoint
1848 switch (dist_units) {
1849 case VIK_UNITS_DISTANCE_MILES:
1850 dist_i = VIK_MILES_TO_METERS(dist_i);
1852 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1853 dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
1855 // VIK_UNITS_DISTANCE_KILOMETRES:
1857 dist_i = dist_i*1000.0;
1861 gdouble dist_current = 0.0;
1862 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1863 gdouble dist_next = 0.0;
1864 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1866 gdouble dist_between_tps = fabs (dist_next - dist_current);
1867 gdouble ratio = 0.0;
1868 // Prevent division by 0 errors
1869 if ( dist_between_tps > 0.0 )
1870 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1872 if ( tp_current && tp_next ) {
1873 // Construct the name based on the distance value
1876 switch (dist_units) {
1877 case VIK_UNITS_DISTANCE_MILES:
1878 units = g_strdup ( _("miles") );
1880 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1881 units = g_strdup ( _("NM") );
1883 // VIK_UNITS_DISTANCE_KILOMETRES:
1885 units = g_strdup ( _("km") );
1889 // Convert for display
1890 dist_i = distance_in_preferred_units ( dist_i );
1892 // Make the precision of the output related to the unit size.
1894 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1895 else if ( index == 1 )
1896 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1898 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1901 struct LatLon ll_current, ll_next;
1902 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1903 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1905 // positional interpolation
1906 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1907 // but should be good enough over the small scale that I anticipate usage on
1908 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1909 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1911 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1914 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1915 fgcolour = gdk_color_to_string ( &(trk->color) );
1917 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1919 // if highlight mode on, then colour the background in the highlight colour
1921 if ( drawing_highlight )
1922 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1924 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1926 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1928 g_free ( fgcolour );
1929 g_free ( bgcolour );
1936 * trw_layer_draw_track_name_labels:
1938 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1940 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1943 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1944 fgcolour = gdk_color_to_string ( &(trk->color) );
1946 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1948 // if highlight mode on, then colour the background in the highlight colour
1950 if ( drawing_highlight )
1951 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1953 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1955 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1957 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1958 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1959 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1960 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1961 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1962 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1964 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1966 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1969 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1970 // No other labels to draw
1973 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1976 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1979 VikCoord begin_coord = tp_begin->coord;
1980 VikCoord end_coord = tp_end->coord;
1982 gboolean done_start_end = FALSE;
1984 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1985 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1987 // This number can be configured via the settings if you really want to change it
1988 gdouble distance_diff;
1989 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1990 distance_diff = 100.0; // Metres
1992 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1993 // Start and end 'close' together so only draw one label at an average location
1994 gint x1, x2, y1, y2;
1995 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1996 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1998 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
2000 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
2001 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
2004 done_start_end = TRUE;
2008 if ( ! done_start_end ) {
2009 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
2010 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
2011 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
2012 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
2013 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
2014 g_free ( name_start );
2016 // Don't draw end label if this is the one being created
2017 if ( trk != dp->vtl->current_track ) {
2018 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
2019 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
2020 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
2021 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
2022 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
2023 g_free ( name_end );
2028 g_free ( fgcolour );
2029 g_free ( bgcolour );
2035 * trw_layer_draw_point_names:
2037 * Draw a point labels along a track
2038 * This might slow things down if there's many tracks being displayed with this on.
2040 static void trw_layer_draw_point_names ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
2042 GList *list = trk->trackpoints;
2044 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2046 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
2047 fgcolour = gdk_color_to_string ( &(trk->color) );
2049 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
2051 if ( drawing_highlight )
2052 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
2054 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
2056 trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord );
2057 while ((list = g_list_next(list)))
2059 tp = VIK_TRACKPOINT(list->data);
2061 trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord );
2063 g_free ( fgcolour );
2064 g_free ( bgcolour );
2067 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
2069 if ( ! track->visible )
2072 /* TODO: this function is a mess, get rid of any redundancy */
2073 GList *list = track->trackpoints;
2075 gboolean useoldvals = TRUE;
2077 gboolean drawpoints;
2079 gboolean drawelevation;
2080 gdouble min_alt, max_alt, alt_diff = 0;
2082 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
2083 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
2086 if ( dp->vtl->drawelevation )
2088 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
2089 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
2090 alt_diff = max_alt - min_alt;
2093 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
2094 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
2095 trw_layer_draw_track ( id, track, dp, TRUE );
2097 if ( draw_track_outline )
2098 drawpoints = drawstops = FALSE;
2100 drawpoints = dp->vtl->drawpoints;
2101 drawstops = dp->vtl->drawstops;
2104 gboolean drawing_highlight = FALSE;
2105 /* Current track - used for creation */
2106 if ( track == dp->vtl->current_track )
2107 main_gc = dp->vtl->current_track_gc;
2109 if ( dp->highlight ) {
2110 /* Draw all tracks of the layer in special colour
2111 NB this supercedes the drawmode */
2112 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2113 drawing_highlight = TRUE;
2115 if ( !drawing_highlight ) {
2116 // Still need to figure out the gc according to the drawing mode:
2117 switch ( dp->vtl->drawmode ) {
2118 case DRAWMODE_BY_TRACK:
2119 if ( dp->vtl->track_1color_gc )
2120 g_object_unref ( dp->vtl->track_1color_gc );
2121 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2122 main_gc = dp->vtl->track_1color_gc;
2125 // Mostly for DRAWMODE_ALL_SAME_COLOR
2126 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2127 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2134 int x, y, oldx, oldy;
2135 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2137 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2139 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2141 // Draw the first point as something a bit different from the normal points
2142 // ATM it's slightly bigger and a triangle
2144 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2145 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2151 gdouble average_speed = 0.0;
2152 gdouble low_speed = 0.0;
2153 gdouble high_speed = 0.0;
2154 // If necessary calculate these values - which is done only once per track redraw
2155 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2156 // the percentage factor away from the average speed determines transistions between the levels
2157 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2158 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2159 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2162 while ((list = g_list_next(list)))
2164 tp = VIK_TRACKPOINT(list->data);
2165 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2167 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2168 // See if in a different lat/lon 'quadrant' so don't draw massively long lines (presumably wrong way around the Earth)
2169 // Mainly to prevent wrong lines drawn when a track crosses the 180 degrees East-West longitude boundary
2170 // (since vik_viewport_draw_line() only copes with pixel value and has no concept of the globe)
2172 (( tp2->coord.east_west < -90.0 && tp->coord.east_west > 90.0 ) ||
2173 ( tp2->coord.east_west > 90.0 && tp->coord.east_west < -90.0 )) ) {
2177 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2178 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2179 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2180 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2181 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2183 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2186 * If points are the same in display coordinates, don't draw.
2188 if ( useoldvals && x == oldx && y == oldy )
2190 // Still need to process points to ensure 'stops' are drawn if required
2191 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2192 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2193 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 );
2198 if ( drawpoints || dp->vtl->drawlines ) {
2199 // setup main_gc for both point and line drawing
2200 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2201 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 ) );
2205 if ( drawpoints && ! draw_track_outline )
2210 * The concept of drawing stops is that a trackpoint
2211 * that is if the next trackpoint has a timestamp far into
2212 * the future, we draw a circle of 6x trackpoint size,
2213 * instead of a rectangle of 2x trackpoint size.
2214 * This is drawn first so the trackpoint will be drawn on top
2217 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2218 /* Stop point. Draw 6x circle. Always in redish colour */
2219 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 );
2221 /* Regular point - draw 2x square. */
2222 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2225 /* Final point - draw 4x circle. */
2226 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 );
2229 if ((!tp->newsegment) && (dp->vtl->drawlines))
2232 /* UTM only: zone check */
2233 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2234 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2237 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2239 if ( draw_track_outline ) {
2240 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2244 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2246 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2248 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2253 tmp[1].y = oldy-FIXALTITUDE(list->data);
2255 tmp[2].y = y-FIXALTITUDE(list->next->data);
2260 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2261 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2263 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2264 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2266 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2271 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2272 // Draw an arrow at the mid point to show the direction of the track
2273 // Code is a rework from vikwindow::draw_ruler()
2274 gint midx = (oldx + x) / 2;
2275 gint midy = (oldy + y) / 2;
2277 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2278 // Avoid divide by zero and ensure at least 1 pixel big
2280 gdouble dx = (oldx - midx) / len;
2281 gdouble dy = (oldy - midy) / len;
2282 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2283 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2293 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2295 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2297 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2299 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2300 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 ));
2304 * If points are the same in display coordinates, don't draw.
2306 if ( x != oldx || y != oldy )
2308 if ( draw_track_outline )
2309 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2311 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2317 * If points are the same in display coordinates, don't draw.
2319 if ( x != oldx && y != oldy )
2321 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2322 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2330 // Labels drawn after the trackpoints, so the labels are on top
2331 if ( dp->vtl->track_draw_labels ) {
2332 if ( track->max_number_dist_labels > 0 ) {
2333 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2335 trw_layer_draw_point_names (dp, track, drawing_highlight );
2337 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2338 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2344 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2346 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2347 trw_layer_draw_track ( id, track, dp, FALSE );
2351 static void cached_pixbuf_free ( CachedPixbuf *cp )
2353 g_object_unref ( G_OBJECT(cp->pixbuf) );
2354 g_free ( cp->image );
2357 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2359 return strcmp ( cp->image, name );
2362 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2365 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2366 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2367 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2370 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2372 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2374 if ( wp->image && dp->vtl->drawimages )
2376 GdkPixbuf *pixbuf = NULL;
2379 if ( dp->vtl->image_alpha == 0)
2382 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2384 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2387 gchar *image = wp->image;
2388 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2389 if ( ! regularthumb )
2391 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2392 image = "\x12\x00"; /* this shouldn't occur naturally. */
2396 CachedPixbuf *cp = NULL;
2397 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2398 if ( dp->vtl->image_size == 128 )
2399 cp->pixbuf = regularthumb;
2402 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2403 g_assert ( cp->pixbuf );
2404 g_object_unref ( G_OBJECT(regularthumb) );
2406 cp->image = g_strdup ( image );
2408 // Apply alpha setting to the image before the pixbuf gets stored in the cache
2409 if ( dp->vtl->image_alpha != 255 )
2410 cp->pixbuf = ui_pixbuf_set_alpha ( cp->pixbuf, dp->vtl->image_alpha );
2412 /* needed so 'click picture' tool knows how big the pic is; we don't
2413 * store it in cp because they may have been freed already. */
2414 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2415 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2417 g_queue_push_head ( dp->vtl->image_cache, cp );
2418 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2419 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2421 pixbuf = cp->pixbuf;
2425 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2431 w = gdk_pixbuf_get_width ( pixbuf );
2432 h = gdk_pixbuf_get_height ( pixbuf );
2434 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2436 if ( dp->highlight ) {
2437 // Highlighted - so draw a little border around the chosen one
2438 // single line seems a little weak so draw 2 of them
2439 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2440 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2441 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2442 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2445 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2447 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2451 // Draw appropriate symbol - either symbol image or simple types
2452 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2453 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 );
2455 else if ( wp == dp->vtl->current_wp ) {
2456 switch ( dp->vtl->wp_symbol ) {
2457 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;
2458 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;
2459 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;
2460 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 );
2461 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 );
2466 switch ( dp->vtl->wp_symbol ) {
2467 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;
2468 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;
2469 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;
2470 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 );
2471 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;
2476 if ( dp->vtl->drawlabels )
2478 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2479 gint label_x, label_y;
2481 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2483 // Could this stored in the waypoint rather than recreating each pass?
2484 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2486 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2487 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2489 // Fallback if parse failure
2490 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2492 g_free ( wp_label_markup );
2494 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2495 label_x = x - width/2;
2496 if ( wp->symbol_pixbuf )
2497 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2499 label_y = y - dp->vtl->wp_size - height - 2;
2501 /* if highlight mode on, then draw background text in highlight colour */
2502 if ( dp->highlight )
2503 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2505 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2506 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2511 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2513 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2514 trw_layer_draw_waypoint ( id, wp, dp );
2518 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2520 static struct DrawingParams dp;
2521 g_assert ( l != NULL );
2523 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2525 if ( l->tracks_visible )
2526 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2528 if ( l->routes_visible )
2529 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2531 if (l->waypoints_visible)
2532 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2535 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2537 // 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
2538 // This may seem slightly inefficient to test each time for every layer
2539 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2540 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2541 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2543 trw_layer_draw_with_highlight ( l, data, FALSE );
2546 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2548 // Check the layer for visibility (including all the parents visibilities)
2549 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2551 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2555 * vik_trw_layer_draw_highlight_item:
2557 * Only handles a single track or waypoint ATM
2558 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2560 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2562 // Check the layer for visibility (including all the parents visibilities)
2563 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2566 static struct DrawingParams dp;
2567 init_drawing_params ( &dp, vtl, vvp, TRUE );
2570 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2572 trw_layer_draw_track_cb ( NULL, trk, &dp );
2574 if ( vtl->waypoints_visible && wpt ) {
2575 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2580 * vik_trw_layer_draw_highlight_item:
2582 * Generally for drawing all tracks or routes or waypoints
2583 * trks may be actually routes
2584 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2586 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2588 // Check the layer for visibility (including all the parents visibilities)
2589 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2592 static struct DrawingParams dp;
2593 init_drawing_params ( &dp, vtl, vvp, TRUE );
2596 gboolean is_routes = (trks == vtl->routes);
2597 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2599 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2602 if ( vtl->waypoints_visible && wpts )
2603 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2606 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2609 if ( vtl->track_bg_gc )
2611 g_object_unref ( vtl->track_bg_gc );
2612 vtl->track_bg_gc = NULL;
2614 if ( vtl->track_1color_gc )
2616 g_object_unref ( vtl->track_1color_gc );
2617 vtl->track_1color_gc = NULL;
2619 if ( vtl->current_track_gc )
2621 g_object_unref ( vtl->current_track_gc );
2622 vtl->current_track_gc = NULL;
2624 if ( vtl->current_track_newpoint_gc )
2626 g_object_unref ( vtl->current_track_newpoint_gc );
2627 vtl->current_track_newpoint_gc = NULL;
2630 if ( ! vtl->track_gc )
2632 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2633 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2634 g_array_free ( vtl->track_gc, TRUE );
2635 vtl->track_gc = NULL;
2638 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2640 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2641 gint width = vtl->line_thickness;
2643 if ( vtl->track_gc )
2644 trw_layer_free_track_gcs ( vtl );
2646 if ( vtl->track_bg_gc )
2647 g_object_unref ( vtl->track_bg_gc );
2648 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2650 // Ensure new track drawing heeds line thickness setting
2651 // however always have a minium of 2, as 1 pixel is really narrow
2652 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2654 if ( vtl->current_track_gc )
2655 g_object_unref ( vtl->current_track_gc );
2656 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2657 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2659 // 'newpoint' gc is exactly the same as the current track gc
2660 if ( vtl->current_track_newpoint_gc )
2661 g_object_unref ( vtl->current_track_newpoint_gc );
2662 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2663 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2665 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2667 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2668 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2670 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2671 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2672 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2674 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2676 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2679 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2681 VikTrwLayer *rv = trw_layer_new1 ( vp );
2682 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2684 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2685 /* early exit, as the rest is GUI related */
2689 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2690 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2692 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2693 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2695 trw_layer_new_track_gcs ( rv, vp );
2697 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2698 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2699 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2700 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2702 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2704 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2709 #define SMALL_ICON_SIZE 18
2711 * Can accept a null symbol, and may return null value
2713 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2715 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2716 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2717 // So needing a small icon for the treeview may need some resizing:
2718 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2719 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2723 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2725 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2727 GdkPixbuf *pixbuf = NULL;
2729 if ( track->has_color ) {
2730 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2731 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2732 // Here is some magic found to do the conversion
2733 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2734 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2735 ((track->color.green & 0xff00) << 8) |
2736 (track->color.blue & 0xff00);
2738 gdk_pixbuf_fill ( pixbuf, pixel );
2741 time_t timestamp = 0;
2742 VikTrackpoint *tpt = vik_track_get_tp_first(track);
2743 if ( tpt && tpt->has_timestamp )
2744 timestamp = tpt->timestamp;
2746 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 );
2749 g_object_unref (pixbuf);
2751 *new_iter = *((GtkTreeIter *) pass_along[1]);
2752 if ( track->is_route )
2753 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2755 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2757 if ( ! track->visible )
2758 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2761 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2763 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2765 time_t timestamp = 0;
2766 if ( wp->has_timestamp )
2767 timestamp = wp->timestamp;
2769 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 );
2771 *new_iter = *((GtkTreeIter *) pass_along[1]);
2772 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2774 if ( ! wp->visible )
2775 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2778 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2780 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, FALSE, 0 );
2783 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2785 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, FALSE, 0 );
2788 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2790 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, FALSE, 0 );
2793 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2796 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2798 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2799 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2801 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2803 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2806 if ( g_hash_table_size (vtl->routes) > 0 ) {
2807 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2809 pass_along[0] = &(vtl->routes_iter);
2810 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2812 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2814 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2817 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2818 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2820 pass_along[0] = &(vtl->waypoints_iter);
2821 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2823 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2825 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2828 trw_layer_verify_thumbnails ( vtl );
2830 trw_layer_sort_all ( vtl );
2833 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2837 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2838 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2839 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2840 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2842 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2844 return (t->visible ^= 1);
2848 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2850 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2852 return (t->visible ^= 1);
2856 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2858 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2860 return (t->visible ^= 1);
2870 * Return a property about tracks for this layer
2872 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2874 return vtl->line_thickness;
2878 * Build up multiple routes information
2880 static void trw_layer_routes_tooltip ( const gpointer id, VikTrack *tr, gdouble *length )
2882 *length = *length + vik_track_get_length (tr);
2885 // Structure to hold multiple track information for a layer
2894 * Build up layer multiple track information via updating the tooltip_tracks structure
2896 static void trw_layer_tracks_tooltip ( const gpointer id, VikTrack *tr, tooltip_tracks *tt )
2898 tt->length = tt->length + vik_track_get_length (tr);
2900 // Ensure times are available
2901 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2902 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2903 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2904 if ( trkpt_last->has_timestamp ) {
2906 t1 = vik_track_get_tp_first(tr)->timestamp;
2907 t2 = trkpt_last->timestamp;
2909 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2910 // Hence initialize to the first 'proper' value
2911 if ( tt->start_time == 0 )
2912 tt->start_time = t1;
2913 if ( tt->end_time == 0 )
2916 // Update find the earliest / last times
2917 if ( t1 < tt->start_time )
2918 tt->start_time = t1;
2919 if ( t2 > tt->end_time )
2922 // Keep track of total time
2923 // there maybe gaps within a track (eg segments)
2924 // but this should be generally good enough for a simple indicator
2925 tt->duration = tt->duration + (int)(t2-t1);
2931 * Generate tooltip text for the layer.
2932 * This is relatively complicated as it considers information for
2933 * no tracks, a single track or multiple tracks
2934 * (which may or may not have timing information)
2936 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2947 static gchar tmp_buf[128];
2950 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2952 // Safety check - I think these should always be valid
2953 if ( vtl->tracks && vtl->waypoints ) {
2954 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2955 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2957 GDate* gdate_start = g_date_new ();
2958 g_date_set_time_t (gdate_start, tt.start_time);
2960 GDate* gdate_end = g_date_new ();
2961 g_date_set_time_t (gdate_end, tt.end_time);
2963 if ( g_date_compare (gdate_start, gdate_end) ) {
2964 // Dates differ so print range on separate line
2965 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2966 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2967 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2970 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2971 if ( tt.start_time != 0 )
2972 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2976 if ( tt.length > 0.0 ) {
2977 gdouble len_in_units;
2979 // Setup info dependent on distance units
2980 switch ( a_vik_get_units_distance() ) {
2981 case VIK_UNITS_DISTANCE_MILES:
2982 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2983 len_in_units = VIK_METERS_TO_MILES(tt.length);
2985 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2986 g_snprintf (tbuf4, sizeof(tbuf4), "NM");
2987 len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length);
2990 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2991 len_in_units = tt.length/1000.0;
2995 // Timing information if available
2997 if ( tt.duration > 0 ) {
2998 g_snprintf (tbuf1, sizeof(tbuf1),
2999 _(" in %d:%02d hrs:mins"),
3000 (int)(tt.duration/3600), (int)round(tt.duration/60.0)%60);
3002 g_snprintf (tbuf2, sizeof(tbuf2),
3003 _("\n%sTotal Length %.1f %s%s"),
3004 tbuf3, len_in_units, tbuf4, tbuf1);
3008 gdouble rlength = 0.0;
3009 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_routes_tooltip, &rlength );
3010 if ( rlength > 0.0 ) {
3011 gdouble len_in_units;
3012 // Setup info dependent on distance units
3013 switch ( a_vik_get_units_distance() ) {
3014 case VIK_UNITS_DISTANCE_MILES:
3015 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
3016 len_in_units = VIK_METERS_TO_MILES(rlength);
3018 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3019 g_snprintf (tbuf4, sizeof(tbuf4), "NM");
3020 len_in_units = VIK_METERS_TO_NAUTICAL_MILES(rlength);
3023 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
3024 len_in_units = rlength/1000.0;
3027 g_snprintf (tbuf1, sizeof(tbuf1), _("\nTotal route length %.1f %s"), len_in_units, tbuf4);
3030 // Put together all the elements to form compact tooltip text
3031 g_snprintf (tmp_buf, sizeof(tmp_buf),
3032 _("Tracks: %d - Waypoints: %d - Routes: %d%s%s"),
3033 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2, tbuf1);
3035 g_date_free (gdate_start);
3036 g_date_free (gdate_end);
3042 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
3046 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3048 // Very simple tooltip - may expand detail in the future...
3049 static gchar tmp_buf[32];
3050 g_snprintf (tmp_buf, sizeof(tmp_buf),
3052 g_hash_table_size (l->tracks));
3056 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3058 // Very simple tooltip - may expand detail in the future...
3059 static gchar tmp_buf[32];
3060 g_snprintf (tmp_buf, sizeof(tmp_buf),
3062 g_hash_table_size (l->routes));
3067 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3068 // Same tooltip for a route
3069 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3072 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3073 tr = g_hash_table_lookup ( l->tracks, sublayer );
3075 tr = g_hash_table_lookup ( l->routes, sublayer );
3078 // Could be a better way of handling strings - but this works...
3079 gchar time_buf1[20];
3080 gchar time_buf2[20];
3081 time_buf1[0] = '\0';
3082 time_buf2[0] = '\0';
3083 static gchar tmp_buf[100];
3084 // Compact info: Short date eg (11/20/99), duration and length
3085 // Hopefully these are the things that are most useful and so promoted into the tooltip
3086 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
3087 // %x The preferred date representation for the current locale without the time.
3088 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
3089 time_t dur = vik_track_get_duration ( tr, TRUE );
3091 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)(dur/3600), (int)round(dur/60.0)%60 );
3093 // Get length and consider the appropriate distance units
3094 gdouble tr_len = vik_track_get_length(tr);
3095 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3096 switch (dist_units) {
3097 case VIK_UNITS_DISTANCE_KILOMETRES:
3098 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
3100 case VIK_UNITS_DISTANCE_MILES:
3101 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
3103 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3104 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2);
3113 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3115 // Very simple tooltip - may expand detail in the future...
3116 static gchar tmp_buf[32];
3117 g_snprintf (tmp_buf, sizeof(tmp_buf),
3119 g_hash_table_size (l->waypoints));
3123 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3125 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
3126 // NB It's OK to return NULL
3131 return w->description;
3140 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
3143 * set_statusbar_msg_info_trkpt:
3145 * Function to show track point information on the statusbar
3146 * Items displayed is controlled by the settings format code
3148 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
3150 gchar *statusbar_format_code = NULL;
3151 gboolean need2free = FALSE;
3152 VikTrackpoint *trkpt_prev = NULL;
3153 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
3154 // Otherwise use default
3155 statusbar_format_code = g_strdup ( "KEATDN" );
3159 // Format code may want to show speed - so may need previous trkpt to work it out
3160 trkpt_prev = vik_track_get_tp_prev ( vtl->current_tp_track, trkpt );
3163 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, trkpt_prev, vtl->current_tp_track, NAN );
3164 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3168 g_free ( statusbar_format_code );
3172 * Function to show basic waypoint information on the statusbar
3174 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3177 switch (a_vik_get_units_height ()) {
3178 case VIK_UNITS_HEIGHT_FEET:
3179 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3182 //VIK_UNITS_HEIGHT_METRES:
3183 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3187 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3188 // one can easily use the current pointer position to see this if needed
3189 gchar *lat = NULL, *lon = NULL;
3190 static struct LatLon ll;
3191 vik_coord_to_latlon (&(wpt->coord), &ll);
3192 a_coords_latlon_to_string ( &ll, &lat, &lon );
3194 // Combine parts to make overall message
3197 // Add comment if available
3198 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3200 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3201 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3208 * General layer selection function, find out which bit is selected and take appropriate action
3210 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3213 l->current_wp = NULL;
3214 l->current_wp_id = NULL;
3215 trw_layer_cancel_current_tp ( l, FALSE );
3218 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3222 case VIK_TREEVIEW_TYPE_LAYER:
3224 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3225 /* Mark for redraw */
3230 case VIK_TREEVIEW_TYPE_SUBLAYER:
3234 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3236 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3237 /* Mark for redraw */
3241 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3243 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3244 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3245 /* Mark for redraw */
3249 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3251 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3252 /* Mark for redraw */
3256 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3258 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3259 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3260 /* Mark for redraw */
3264 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3266 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3267 /* Mark for redraw */
3271 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3273 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3275 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3276 // Show some waypoint info
3277 set_statusbar_msg_info_wpt ( l, wpt );
3278 /* Mark for redraw */
3285 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3294 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3299 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3304 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3309 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3311 return l->waypoints;
3314 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3316 return vtl->tracks_iters;
3319 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3321 return vtl->routes_iters;
3324 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3326 return vtl->waypoints_iters;
3329 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3331 return ! ( g_hash_table_size ( vtl->tracks ) ||
3332 g_hash_table_size ( vtl->routes ) ||
3333 g_hash_table_size ( vtl->waypoints ) );
3336 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3338 return vtl->tracks_visible;
3341 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3343 return vtl->routes_visible;
3346 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3348 return vtl->waypoints_visible;
3352 * ATM use a case sensitive find
3353 * Finds the first one
3355 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3357 if ( wp && wp->name )
3358 if ( ! strcmp ( wp->name, name ) )
3364 * Get waypoint by name - not guaranteed to be unique
3365 * Finds the first one
3367 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3369 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3373 * ATM use a case sensitive find
3374 * Finds the first one
3376 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3378 if ( trk && trk->name )
3379 if ( ! strcmp ( trk->name, name ) )
3385 * Get track by name - not guaranteed to be unique
3386 * Finds the first one
3388 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3390 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3394 * Get route by name - not guaranteed to be unique
3395 * Finds the first one
3397 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3399 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3402 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3404 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3405 maxmin[0].lat = trk->bbox.north;
3406 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3407 maxmin[1].lat = trk->bbox.south;
3408 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3409 maxmin[0].lon = trk->bbox.east;
3410 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3411 maxmin[1].lon = trk->bbox.west;
3414 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3416 // Continually reuse maxmin to find the latest maximum and minimum values
3417 // First set to waypoints bounds
3418 maxmin[0].lat = vtl->waypoints_bbox.north;
3419 maxmin[1].lat = vtl->waypoints_bbox.south;
3420 maxmin[0].lon = vtl->waypoints_bbox.east;
3421 maxmin[1].lon = vtl->waypoints_bbox.west;
3422 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3423 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3426 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3428 /* 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... */
3429 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3430 trw_layer_find_maxmin (vtl, maxmin);
3431 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3435 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3436 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3441 static void trw_layer_centerize ( menu_array_layer values )
3443 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3445 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3446 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3448 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3451 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3453 vu_zoom_to_show_latlons ( vtl->coord_mode, vvp, maxmin );
3456 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3458 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3459 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3460 trw_layer_find_maxmin (vtl, maxmin);
3461 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3464 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3469 static void trw_layer_auto_view ( menu_array_layer values )
3471 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3472 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3473 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3474 vik_layers_panel_emit_update ( vlp );
3477 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3480 static void trw_layer_export_gpspoint ( menu_array_layer values )
3482 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3484 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3486 g_free ( auto_save_name );
3489 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3491 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3493 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3495 g_free ( auto_save_name );
3498 static void trw_layer_export_gpx ( menu_array_layer values )
3500 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3502 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3504 g_free ( auto_save_name );
3507 static void trw_layer_export_kml ( menu_array_layer values )
3509 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3511 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3513 g_free ( auto_save_name );
3516 static void trw_layer_export_geojson ( menu_array_layer values )
3518 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3520 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3522 g_free ( auto_save_name );
3525 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3527 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3528 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3531 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3533 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3536 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3538 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3541 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3543 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3545 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3546 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3548 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3550 if ( !trk || !trk->name )
3553 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3555 gchar *label = NULL;
3556 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3557 label = _("Export Route as GPX");
3559 label = _("Export Track as GPX");
3560 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3562 g_free ( auto_save_name );
3565 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3567 wpu_udata *user_data = udata;
3568 if ( wp == user_data->wp ) {
3569 user_data->uuid = id;
3575 static void trw_layer_goto_wp ( menu_array_layer values )
3577 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3578 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3579 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3580 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3581 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3583 GTK_RESPONSE_REJECT,
3585 GTK_RESPONSE_ACCEPT,
3588 GtkWidget *label, *entry;
3589 label = gtk_label_new(_("Waypoint Name:"));
3590 entry = ui_entry_new ( NULL, GTK_ENTRY_ICON_SECONDARY );
3592 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3593 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3594 gtk_widget_show_all ( dia );
3595 // 'ok' when press return in the entry
3596 g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), dia );
3597 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3599 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3601 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3602 // Find *first* wp with the given name
3603 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3606 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3609 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3610 vik_layers_panel_emit_update ( vlp );
3612 // Find and select on the side panel
3617 // Hmmm, want key of it
3618 gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3620 if ( wpf && udata.uuid ) {
3621 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3622 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3631 gtk_widget_destroy ( dia );
3634 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3636 gchar *default_name = highest_wp_number_get(vtl);
3637 VikWaypoint *wp = vik_waypoint_new();
3638 gchar *returned_name;
3640 wp->coord = *def_coord;
3642 // Attempt to auto set height if DEM data is available
3643 vik_waypoint_apply_dem_data ( wp, TRUE );
3645 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3647 if ( returned_name )
3650 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3651 g_free (default_name);
3652 g_free (returned_name);
3655 g_free (default_name);
3656 vik_waypoint_free(wp);
3660 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3662 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3663 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3664 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3665 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3666 VikViewport *vvp = vik_window_viewport(vw);
3668 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3669 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3670 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3671 trw_layer_calculate_bounds_waypoints ( vtl );
3672 vik_layers_panel_emit_update ( vlp );
3675 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3677 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3678 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3679 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3681 trw_layer_find_maxmin (vtl, maxmin);
3682 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3683 trw_layer_calculate_bounds_waypoints ( vtl );
3684 vik_layers_panel_emit_update ( vlp );
3687 #ifdef VIK_CONFIG_GEOTAG
3688 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3690 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3692 // Update directly - not changing the mtime
3693 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3696 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3698 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3701 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3705 * Use code in separate file for this feature as reasonably complex
3707 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3709 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3710 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3711 // Unset so can be reverified later if necessary
3712 vtl->has_verified_thumbnails = FALSE;
3714 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3720 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3722 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3723 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3725 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3731 static void trw_layer_geotagging ( menu_array_layer values )
3733 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3734 // Unset so can be reverified later if necessary
3735 vtl->has_verified_thumbnails = FALSE;
3737 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3744 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3746 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3748 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3749 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3750 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3751 VikViewport *vvp = vik_window_viewport(vw);
3753 vik_datasource_mode_t mode = datasource->mode;
3754 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3755 mode = VIK_DATASOURCE_ADDTOLAYER;
3756 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3760 * Acquire into this TRW Layer straight from GPS Device
3762 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3764 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3768 * Acquire into this TRW Layer from Directions
3770 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3772 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3776 * Acquire into this TRW Layer from an entered URL
3778 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3780 trw_layer_acquire ( values, &vik_datasource_url_interface );
3783 #ifdef VIK_CONFIG_OPENSTREETMAP
3785 * Acquire into this TRW Layer from OSM
3787 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3789 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3793 * Acquire into this TRW Layer from OSM for 'My' Traces
3795 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3797 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3801 #ifdef VIK_CONFIG_GEOCACHES
3803 * Acquire into this TRW Layer from Geocaching.com
3805 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3807 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3811 #ifdef VIK_CONFIG_GEOTAG
3813 * Acquire into this TRW Layer from images
3815 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3817 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3819 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3821 // Reverify thumbnails as they may have changed
3822 vtl->has_verified_thumbnails = FALSE;
3823 trw_layer_verify_thumbnails ( vtl );
3828 * Acquire into this TRW Layer from any GPS Babel supported file
3830 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3832 trw_layer_acquire ( values, &vik_datasource_file_interface );
3835 static void trw_layer_gps_upload ( menu_array_layer values )
3837 menu_array_sublayer data;
3839 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3841 data[MA_VTL] = values[MA_VTL];
3842 data[MA_VLP] = values[MA_VLP];
3844 trw_layer_gps_upload_any ( data );
3848 * If pass_along[3] is defined that this will upload just that track
3850 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3852 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3853 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3855 // May not actually get a track here as values[2&3] can be null
3856 VikTrack *track = NULL;
3857 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3858 gboolean xfer_all = FALSE;
3860 if ( values[MA_SUBTYPE] ) {
3862 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3863 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3866 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3867 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3870 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3873 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3877 else if ( !values[MA_CONFIRM] )
3878 xfer_all = TRUE; // i.e. whole layer
3880 if (track && !track->visible) {
3881 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3885 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3886 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3887 GTK_DIALOG_DESTROY_WITH_PARENT,
3889 GTK_RESPONSE_ACCEPT,
3891 GTK_RESPONSE_REJECT,
3894 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3895 GtkWidget *response_w = NULL;
3896 #if GTK_CHECK_VERSION (2, 20, 0)
3897 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3901 gtk_widget_grab_focus ( response_w );
3903 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3905 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3906 datasource_gps_clean_up ( dgs );
3907 gtk_widget_destroy ( dialog );
3911 // Get info from reused datasource dialog widgets
3912 gchar* protocol = datasource_gps_get_protocol ( dgs );
3913 gchar* port = datasource_gps_get_descriptor ( dgs );
3914 // NB don't free the above strings as they're references to values held elsewhere
3915 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3916 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3917 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3918 gboolean turn_off = datasource_gps_get_off ( dgs );
3920 gtk_widget_destroy ( dialog );
3922 // When called from the viewport - work the corresponding layerspanel:
3924 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3927 // Apply settings to transfer to the GPS device
3934 vik_layers_panel_get_viewport (vlp),
3942 static void trw_layer_new_wp ( menu_array_layer values )
3944 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3945 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3946 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3947 instead return true if you want to update. */
3948 if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) ) {
3949 trw_layer_calculate_bounds_waypoints ( vtl );
3950 if ( VIK_LAYER(vtl)->visible )
3951 vik_layers_panel_emit_update ( vlp );
3955 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3957 vtl->current_track = vik_track_new();
3958 vik_track_set_defaults ( vtl->current_track );
3959 vtl->current_track->visible = TRUE;
3960 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3961 // Create track with the preferred colour from the layer properties
3962 vtl->current_track->color = vtl->track_color;
3964 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3965 vtl->current_track->has_color = TRUE;
3966 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3969 static void trw_layer_new_track ( menu_array_layer values )
3971 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3973 if ( ! vtl->current_track ) {
3974 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3975 new_track_create_common ( vtl, name );
3978 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3982 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3984 vtl->current_track = vik_track_new();
3985 vik_track_set_defaults ( vtl->current_track );
3986 vtl->current_track->visible = TRUE;
3987 vtl->current_track->is_route = TRUE;
3988 // By default make all routes red
3989 vtl->current_track->has_color = TRUE;
3990 gdk_color_parse ( "red", &vtl->current_track->color );
3991 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3994 static void trw_layer_new_route ( menu_array_layer values )
3996 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3998 if ( ! vtl->current_track ) {
3999 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
4000 new_route_create_common ( vtl, name );
4002 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
4006 static void trw_layer_auto_routes_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->routes) > 0 ) {
4012 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4013 g_hash_table_foreach ( vtl->routes, (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 );
4020 static void trw_layer_finish_track ( menu_array_layer values )
4022 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4023 vtl->current_track = NULL;
4024 vtl->route_finder_started = FALSE;
4025 vik_layer_emit_update ( VIK_LAYER(vtl) );
4028 static void trw_layer_auto_tracks_view ( menu_array_layer values )
4030 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4031 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
4033 if ( g_hash_table_size (vtl->tracks) > 0 ) {
4034 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4035 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
4036 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
4037 vik_layers_panel_emit_update ( vlp );
4041 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
4043 /* NB do not care if wp is visible or not */
4044 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
4047 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
4049 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4050 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
4052 /* Only 1 waypoint - jump straight to it */
4053 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
4054 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
4055 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
4057 /* If at least 2 waypoints - find center and then zoom to fit */
4058 else if ( g_hash_table_size (vtl->waypoints) > 1 )
4060 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4061 maxmin[0].lat = vtl->waypoints_bbox.north;
4062 maxmin[1].lat = vtl->waypoints_bbox.south;
4063 maxmin[0].lon = vtl->waypoints_bbox.east;
4064 maxmin[1].lon = vtl->waypoints_bbox.west;
4065 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
4068 vik_layers_panel_emit_update ( vlp );
4071 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
4073 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
4076 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
4078 if ( values[MA_MISC] ) {
4079 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
4080 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
4084 static GtkWidget* create_external_submenu ( GtkMenu *menu )
4086 GtkWidget *external_submenu = gtk_menu_new ();
4087 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") );
4088 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
4089 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4090 gtk_widget_show ( item );
4091 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu );
4092 return external_submenu;
4095 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
4097 static menu_array_layer pass_along;
4099 GtkWidget *export_submenu;
4100 pass_along[MA_VTL] = vtl;
4101 pass_along[MA_VLP] = vlp;
4103 item = gtk_menu_item_new();
4104 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4105 gtk_widget_show ( item );
4107 if ( vtl->current_track ) {
4108 if ( vtl->current_track->is_route )
4109 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
4111 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
4112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
4113 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4114 gtk_widget_show ( item );
4117 item = gtk_menu_item_new ();
4118 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4119 gtk_widget_show ( item );
4122 /* Now with icons */
4123 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
4124 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4125 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
4126 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4127 gtk_widget_show ( item );
4129 GtkWidget *view_submenu = gtk_menu_new();
4130 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
4131 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
4132 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4133 gtk_widget_show ( item );
4134 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
4136 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
4137 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4138 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4139 gtk_widget_show ( item );
4141 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4143 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4144 gtk_widget_show ( item );
4146 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4148 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4149 gtk_widget_show ( item );
4151 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4152 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4154 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4155 gtk_widget_show ( item );
4157 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4159 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4160 gtk_widget_show ( item );
4162 export_submenu = gtk_menu_new ();
4163 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4164 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4165 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4166 gtk_widget_show ( item );
4167 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4169 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4171 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4172 gtk_widget_show ( item );
4174 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4176 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4177 gtk_widget_show ( item );
4179 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4181 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4182 gtk_widget_show ( item );
4184 if ( a_babel_available () ) {
4185 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4187 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4188 gtk_widget_show ( item );
4191 if ( have_geojson_export ) {
4192 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4193 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4194 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4195 gtk_widget_show ( item );
4198 if ( a_babel_available () ) {
4199 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4201 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4202 gtk_widget_show ( item );
4205 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4206 item = gtk_menu_item_new_with_mnemonic ( external1 );
4207 g_free ( external1 );
4208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4209 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4210 gtk_widget_show ( item );
4212 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4213 item = gtk_menu_item_new_with_mnemonic ( external2 );
4214 g_free ( external2 );
4215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4216 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4217 gtk_widget_show ( item );
4219 GtkWidget *new_submenu = gtk_menu_new();
4220 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4221 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4222 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4223 gtk_widget_show(item);
4224 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4226 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4227 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4228 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4229 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4230 gtk_widget_show ( item );
4232 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4233 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4234 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4235 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4236 gtk_widget_show ( item );
4237 // Make it available only when a new track *not* already in progress
4238 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4240 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4243 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4244 gtk_widget_show ( item );
4245 // Make it available only when a new track *not* already in progress
4246 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4248 #ifdef VIK_CONFIG_GEOTAG
4249 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4250 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4251 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4252 gtk_widget_show ( item );
4255 GtkWidget *acquire_submenu = gtk_menu_new ();
4256 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4257 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4258 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4259 gtk_widget_show ( item );
4260 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4262 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4264 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4265 gtk_widget_show ( item );
4267 /* FIXME: only add menu when at least a routing engine has support for Directions */
4268 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4269 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4270 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4271 gtk_widget_show ( item );
4273 #ifdef VIK_CONFIG_OPENSTREETMAP
4274 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4275 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4276 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4277 gtk_widget_show ( item );
4279 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4280 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4281 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4282 gtk_widget_show ( item );
4285 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4287 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4288 gtk_widget_show ( item );
4290 #ifdef VIK_CONFIG_GEONAMES
4291 GtkWidget *wikipedia_submenu = gtk_menu_new();
4292 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4293 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4294 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4295 gtk_widget_show(item);
4296 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4298 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4299 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4300 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4301 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4302 gtk_widget_show ( item );
4304 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4305 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4306 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4307 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4308 gtk_widget_show ( item );
4311 #ifdef VIK_CONFIG_GEOCACHES
4312 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4314 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4315 gtk_widget_show ( item );
4318 #ifdef VIK_CONFIG_GEOTAG
4319 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4320 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4321 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4322 gtk_widget_show ( item );
4325 if ( a_babel_available () ) {
4326 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4328 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4329 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4330 gtk_widget_show ( item );
4333 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4335 GtkWidget *upload_submenu = gtk_menu_new ();
4336 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4337 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4338 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4339 gtk_widget_show ( item );
4340 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4342 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4343 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4344 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4345 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4346 gtk_widget_show ( item );
4348 #ifdef VIK_CONFIG_OPENSTREETMAP
4349 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4350 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4351 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4352 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4353 gtk_widget_show ( item );
4356 GtkWidget *delete_submenu = gtk_menu_new ();
4357 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4358 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4359 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4360 gtk_widget_show ( item );
4361 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4363 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4364 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4365 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4366 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4367 gtk_widget_show ( item );
4369 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4370 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4372 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4373 gtk_widget_show ( item );
4375 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4376 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4378 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4379 gtk_widget_show ( item );
4381 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4382 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4383 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4384 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4385 gtk_widget_show ( item );
4387 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4388 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4389 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4390 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4391 gtk_widget_show ( item );
4393 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4394 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4395 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4396 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4397 gtk_widget_show ( item );
4399 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4400 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4402 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4403 gtk_widget_show ( item );
4406 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4407 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4409 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4410 gtk_widget_show ( item );
4413 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4414 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4415 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4416 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4417 gtk_widget_show ( item );
4418 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4420 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4421 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4423 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4424 gtk_widget_show ( item );
4425 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4427 GtkWidget *external_submenu = create_external_submenu ( menu );
4428 // TODO: Should use selected layer's centre - rather than implicitly using the current viewport
4429 vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (external_submenu), NULL );
4432 // Fake Waypoint UUIDs vi simple increasing integer
4433 static guint wp_uuid = 0;
4435 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4439 vik_waypoint_set_name (wp, name);
4441 if ( VIK_LAYER(vtl)->realized )
4443 // Do we need to create the sublayer:
4444 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4445 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4448 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4450 time_t timestamp = 0;
4451 if ( wp->has_timestamp )
4452 timestamp = wp->timestamp;
4454 // Visibility column always needed for waypoints
4455 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 );
4457 // Actual setting of visibility dependent on the waypoint
4458 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4460 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4462 // Sort now as post_read is not called on a realized waypoint
4463 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4466 highest_wp_number_add_wp(vtl, name);
4467 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4471 // Fake Track UUIDs vi simple increasing integer
4472 static guint tr_uuid = 0;
4474 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4478 vik_track_set_name (t, name);
4480 if ( VIK_LAYER(vtl)->realized )
4482 // Do we need to create the sublayer:
4483 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4484 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4487 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4489 time_t timestamp = 0;
4490 VikTrackpoint *tpt = vik_track_get_tp_first(t);
4491 if ( tpt && tpt->has_timestamp )
4492 timestamp = tpt->timestamp;
4494 // Visibility column always needed for tracks
4495 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 );
4497 // Actual setting of visibility dependent on the track
4498 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4500 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4502 // Sort now as post_read is not called on a realized track
4503 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4506 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4508 trw_layer_update_treeview ( vtl, t );
4511 // Fake Route UUIDs vi simple increasing integer
4512 static guint rt_uuid = 0;
4514 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4518 vik_track_set_name (t, name);
4520 if ( VIK_LAYER(vtl)->realized )
4522 // Do we need to create the sublayer:
4523 if ( g_hash_table_size (vtl->routes) == 0 ) {
4524 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4527 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4528 // Visibility column always needed for routes
4529 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
4530 // Actual setting of visibility dependent on the route
4531 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4533 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4535 // Sort now as post_read is not called on a realized route
4536 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4539 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4541 trw_layer_update_treeview ( vtl, t );
4544 /* to be called whenever a track has been deleted or may have been changed. */
4545 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4547 if (vtl->current_tp_track == trk )
4548 trw_layer_cancel_current_tp ( vtl, FALSE );
4552 * Normally this is done to due the waypoint size preference having changed
4554 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4556 GHashTableIter iter;
4557 gpointer key, value;
4560 g_hash_table_iter_init ( &iter, vtl->waypoints );
4561 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4562 VikWaypoint *wp = VIK_WAYPOINT(value);
4564 // Reapply symbol setting to update the pixbuf
4565 gchar *tmp_symbol = g_strdup ( wp->symbol );
4566 vik_waypoint_set_symbol ( wp, tmp_symbol );
4567 g_free ( tmp_symbol );
4573 * trw_layer_new_unique_sublayer_name:
4575 * Allocates a unique new name
4577 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4580 gchar *newname = g_strdup(name);
4585 switch ( sublayer_type ) {
4586 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4587 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4589 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4590 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4593 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4596 // If found a name already in use try adding 1 to it and we try again
4598 const gchar *corename = newname;
4600 // If name is already of the form text#N
4601 // set name to text and i to N+1
4602 gchar **tokens = g_regex_split_simple ( "#(\\d+)", newname, G_REGEX_CASELESS, 0 );
4604 corename = tokens[0];
4606 newi = atoi ( tokens[1] ) + 1;
4609 gchar *new_newname = g_strdup_printf("%s#%d", corename, newi);
4610 g_strfreev ( tokens );
4612 newname = new_newname;
4615 } while ( id != NULL);
4620 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4622 // No more uniqueness of name forced when loading from a file
4623 // This now makes this function a little redunant as we just flow the parameters through
4624 vik_trw_layer_add_waypoint ( vtl, name, wp );
4627 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4629 if ( vtl->route_finder_append && vtl->current_track ) {
4630 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4632 // enforce end of current track equal to start of tr
4633 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4634 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4635 if ( cur_end && new_start ) {
4636 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4637 vik_track_add_trackpoint ( vtl->current_track,
4638 vik_trackpoint_copy ( cur_end ),
4643 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4644 vik_track_free ( tr );
4645 vtl->route_finder_append = FALSE; /* this means we have added it */
4648 // No more uniqueness of name forced when loading from a file
4650 vik_trw_layer_add_route ( vtl, name, tr );
4652 vik_trw_layer_add_track ( vtl, name, tr );
4654 if ( vtl->route_finder_check_added_track ) {
4655 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4656 vtl->route_finder_added_track = tr;
4661 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4663 *l = g_list_append(*l, id);
4667 * Move an item from one TRW layer to another TRW layer
4669 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4671 // When an item is moved the name is checked to see if it clashes with an existing name
4672 // in the destination layer and if so then it is allocated a new name
4674 // TODO reconsider strategy when moving within layer (if anything...)
4675 if ( vtl_src == vtl_dest )
4678 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4679 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4681 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4683 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4684 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4686 vik_trw_layer_delete_track ( vtl_src, trk );
4687 // Reset layer timestamps in case they have now changed
4688 vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
4689 vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
4692 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4693 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4695 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4697 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4698 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4700 vik_trw_layer_delete_route ( vtl_src, trk );
4703 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4704 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4706 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4708 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4709 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4711 trw_layer_delete_waypoint ( vtl_src, wp );
4713 // Recalculate bounds even if not renamed as maybe dragged between layers
4714 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4715 trw_layer_calculate_bounds_waypoints ( vtl_src );
4716 // Reset layer timestamps in case they have now changed
4717 vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
4718 vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
4722 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4724 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4725 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4727 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4728 GList *items = NULL;
4731 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4732 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4734 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4735 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4737 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4738 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4743 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4744 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4745 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4746 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4748 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4755 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4756 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4760 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4762 trku_udata *user_data = udata;
4763 if ( trk == user_data->trk ) {
4764 user_data->uuid = id;
4770 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4772 gboolean was_visible = FALSE;
4773 if ( trk && trk->name ) {
4775 if ( trk == vtl->current_track ) {
4776 vtl->current_track = NULL;
4777 vtl->current_tp_track = NULL;
4778 vtl->current_tp_id = NULL;
4779 vtl->moving_tp = FALSE;
4780 vtl->route_finder_started = FALSE;
4783 was_visible = trk->visible;
4785 if ( trk == vtl->route_finder_added_track )
4786 vtl->route_finder_added_track = NULL;
4792 // Hmmm, want key of it
4793 gpointer trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4795 if ( trkf && udata.uuid ) {
4796 /* could be current_tp, so we have to check */
4797 trw_layer_cancel_tps_of_track ( vtl, trk );
4799 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4802 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4803 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4804 g_hash_table_remove ( vtl->tracks, udata.uuid );
4806 // If last sublayer, then remove sublayer container
4807 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4808 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4811 // Incase it was selected (no item delete signal ATM)
4812 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4818 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4820 gboolean was_visible = FALSE;
4822 if ( trk && trk->name ) {
4824 if ( trk == vtl->current_track ) {
4825 vtl->current_track = NULL;
4826 vtl->current_tp_track = NULL;
4827 vtl->current_tp_id = NULL;
4828 vtl->moving_tp = FALSE;
4831 was_visible = trk->visible;
4833 if ( trk == vtl->route_finder_added_track )
4834 vtl->route_finder_added_track = NULL;
4840 // Hmmm, want key of it
4841 gpointer trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4843 if ( trkf && udata.uuid ) {
4844 /* could be current_tp, so we have to check */
4845 trw_layer_cancel_tps_of_track ( vtl, trk );
4847 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4850 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4851 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4852 g_hash_table_remove ( vtl->routes, udata.uuid );
4854 // If last sublayer, then remove sublayer container
4855 if ( g_hash_table_size (vtl->routes) == 0 ) {
4856 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4859 // Incase it was selected (no item delete signal ATM)
4860 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4866 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4868 gboolean was_visible = FALSE;
4870 if ( wp && wp->name ) {
4872 if ( wp == vtl->current_wp ) {
4873 vtl->current_wp = NULL;
4874 vtl->current_wp_id = NULL;
4875 vtl->moving_wp = FALSE;
4878 was_visible = wp->visible;
4884 // Hmmm, want key of it
4885 gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4887 if ( wpf && udata.uuid ) {
4888 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4891 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4892 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4894 highest_wp_number_remove_wp(vtl, wp->name);
4895 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4897 // If last sublayer, then remove sublayer container
4898 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4899 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4902 // Incase it was selected (no item delete signal ATM)
4903 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4911 // Only for temporary use by trw_layer_delete_waypoint_by_name
4912 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4914 wpu_udata *user_data = udata;
4915 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4916 user_data->uuid = id;
4923 * Delete a waypoint by the given name
4924 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4925 * as there be multiple waypoints with the same name
4927 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4930 // Fake a waypoint with the given name
4931 udata.wp = vik_waypoint_new ();
4932 vik_waypoint_set_name (udata.wp, name);
4933 // Currently only the name is used in this waypoint find function
4936 // Hmmm, want key of it
4937 gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4939 vik_waypoint_free (udata.wp);
4941 if ( wpf && udata.uuid )
4942 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4948 VikTrack *trk; // input
4949 gpointer uuid; // output
4952 // Only for temporary use by trw_layer_delete_track_by_name
4953 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4955 tpu_udata *user_data = udata;
4956 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4957 user_data->uuid = id;
4964 * Delete a track by the given name
4965 * NOTE: ATM this will delete the first encountered Track with the specified name
4966 * as there may be multiple tracks with the same name within the specified hash table
4968 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4971 // Fake a track with the given name
4972 udata.trk = vik_track_new ();
4973 vik_track_set_name (udata.trk, name);
4974 // Currently only the name is used in this waypoint find function
4977 // Hmmm, want key of it
4978 gpointer trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4980 vik_track_free (udata.trk);
4982 if ( trkf && udata.uuid ) {
4983 // This could be a little better written...
4984 if ( vtl->tracks == ht_tracks )
4985 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4986 if ( vtl->routes == ht_tracks )
4987 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4994 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4996 vik_treeview_item_delete (vt, it );
4999 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
5002 vtl->current_track = NULL;
5003 vtl->route_finder_added_track = NULL;
5004 if (vtl->current_tp_track)
5005 trw_layer_cancel_current_tp(vtl, FALSE);
5007 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
5008 g_hash_table_remove_all(vtl->routes_iters);
5010 if ( g_hash_table_size (vtl->routes) > 0 )
5011 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
5012 g_hash_table_remove_all(vtl->routes);
5014 vik_layer_emit_update ( VIK_LAYER(vtl) );
5017 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
5020 vtl->current_track = NULL;
5021 vtl->route_finder_added_track = NULL;
5022 if (vtl->current_tp_track)
5023 trw_layer_cancel_current_tp(vtl, FALSE);
5025 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
5026 g_hash_table_remove_all(vtl->tracks_iters);
5028 if ( g_hash_table_size (vtl->tracks) > 0 )
5029 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
5030 g_hash_table_remove_all(vtl->tracks);
5032 vik_layer_emit_update ( VIK_LAYER(vtl) );
5035 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
5037 vtl->current_wp = NULL;
5038 vtl->current_wp_id = NULL;
5039 vtl->moving_wp = FALSE;
5041 highest_wp_number_reset(vtl);
5043 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
5044 g_hash_table_remove_all(vtl->waypoints_iters);
5046 if ( g_hash_table_size (vtl->waypoints) > 0 )
5047 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
5048 g_hash_table_remove_all(vtl->waypoints);
5050 vik_layer_emit_update ( VIK_LAYER(vtl) );
5053 static void trw_layer_delete_all_tracks ( menu_array_layer values )
5055 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5056 // Get confirmation from the user
5057 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5058 _("Are you sure you want to delete all tracks in %s?"),
5059 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5060 vik_trw_layer_delete_all_tracks (vtl);
5063 static void trw_layer_delete_all_routes ( menu_array_layer values )
5065 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5066 // Get confirmation from the user
5067 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5068 _("Are you sure you want to delete all routes in %s?"),
5069 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5070 vik_trw_layer_delete_all_routes (vtl);
5073 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
5075 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5076 // Get confirmation from the user
5077 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5078 _("Are you sure you want to delete all waypoints in %s?"),
5079 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5080 vik_trw_layer_delete_all_waypoints (vtl);
5083 static void trw_layer_delete_item ( menu_array_sublayer values )
5085 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5086 gboolean was_visible = FALSE;
5087 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5089 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5090 if ( wp && wp->name ) {
5091 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5092 // Get confirmation from the user
5093 // Maybe this Waypoint Delete should be optional as is it could get annoying...
5094 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5095 _("Are you sure you want to delete the waypoint \"%s\"?"),
5098 was_visible = trw_layer_delete_waypoint ( vtl, wp );
5099 trw_layer_calculate_bounds_waypoints ( vtl );
5100 // Reset layer timestamp in case it has now changed
5101 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
5104 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5106 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5107 if ( trk && trk->name ) {
5108 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5109 // Get confirmation from the user
5110 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5111 _("Are you sure you want to delete the track \"%s\"?"),
5114 was_visible = vik_trw_layer_delete_track ( vtl, trk );
5115 // Reset layer timestamp in case it has now changed
5116 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
5121 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5122 if ( trk && trk->name ) {
5123 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5124 // Get confirmation from the user
5125 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5126 _("Are you sure you want to delete the route \"%s\"?"),
5129 was_visible = vik_trw_layer_delete_route ( vtl, trk );
5133 vik_layer_emit_update ( VIK_LAYER(vtl) );
5137 * Rename waypoint and maintain corresponding name of waypoint in the treeview
5139 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
5141 vik_waypoint_set_name ( wp, new_name );
5143 // Now update the treeview as well
5148 // Need key of it for treeview update
5149 gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5151 if ( wpf && udataU.uuid ) {
5152 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5155 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
5156 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
5162 * Maintain icon of waypoint in the treeview
5164 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5166 // update the treeview
5171 // Need key of it for treeview update
5172 gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5174 if ( wpf && udataU.uuid ) {
5175 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5178 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5183 static void trw_layer_properties_item ( menu_array_sublayer values )
5185 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5186 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5188 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5190 if ( wp && wp->name )
5192 gboolean updated = FALSE;
5193 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5195 trw_layer_waypoint_rename ( vtl, wp, new_name );
5197 if ( updated && values[MA_TV_ITER] )
5198 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5200 if ( updated && VIK_LAYER(vtl)->visible )
5201 vik_layer_emit_update ( VIK_LAYER(vtl) );
5207 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5208 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5210 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5212 if ( tr && tr->name )
5214 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5225 * trw_layer_track_statistics:
5227 * Show track statistics.
5228 * ATM jump to the stats page in the properties
5229 * TODO: consider separating the stats into an individual dialog?
5231 static void trw_layer_track_statistics ( menu_array_sublayer values )
5233 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5235 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5236 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5238 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5240 if ( trk && trk->name ) {
5241 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5251 * Update the treeview of the track id - primarily to update the icon
5253 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5259 gpointer trkf = NULL;
5260 if ( trk->is_route )
5261 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5263 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5265 if ( trkf && udata.uuid ) {
5267 GtkTreeIter *iter = NULL;
5268 if ( trk->is_route )
5269 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5271 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5274 // TODO: Make this a function
5275 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5276 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5277 ((trk->color.green & 0xff00) << 8) |
5278 (trk->color.blue & 0xff00);
5279 gdk_pixbuf_fill ( pixbuf, pixel );
5280 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5281 g_object_unref (pixbuf);
5288 Parameter 1 -> VikLayersPanel
5289 Parameter 2 -> VikLayer
5290 Parameter 3 -> VikViewport
5292 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5295 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5296 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5299 /* since vlp not set, vl & vvp should be valid instead! */
5301 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5302 vik_layer_emit_update ( VIK_LAYER(vl) );
5307 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5309 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5311 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5312 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5314 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5316 if ( track && track->trackpoints )
5317 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5320 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5322 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5324 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5325 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5327 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5329 if ( track && track->trackpoints )
5331 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5333 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5334 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5335 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5336 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5337 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5341 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5343 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5345 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5346 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5348 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5353 // Converting a track to a route can be a bit more complicated,
5354 // so give a chance to change our minds:
5355 if ( !trk->is_route &&
5356 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5357 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5359 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5360 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5365 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5368 trk_copy->is_route = !trk_copy->is_route;
5370 // ATM can't set name to self - so must create temporary copy
5371 gchar *name = g_strdup ( trk_copy->name );
5373 // Delete old one and then add new one
5374 if ( trk->is_route ) {
5375 vik_trw_layer_delete_route ( vtl, trk );
5376 vik_trw_layer_add_track ( vtl, name, trk_copy );
5379 // Extra route conversion bits...
5380 vik_track_merge_segments ( trk_copy );
5381 vik_track_to_routepoints ( trk_copy );
5383 vik_trw_layer_delete_track ( vtl, trk );
5384 vik_trw_layer_add_route ( vtl, name, trk_copy );
5388 // Update in case color of track / route changes when moving between sublayers
5389 vik_layer_emit_update ( VIK_LAYER(vtl) );
5392 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5394 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5396 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5397 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5399 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5402 vik_track_anonymize_times ( track );
5405 static void trw_layer_interpolate_times ( menu_array_sublayer values )
5407 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5409 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5410 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5412 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5415 vik_track_interpolate_times ( track );
5418 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5420 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5422 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5423 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5425 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5430 vtl->current_track = track;
5431 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);
5433 if ( track->trackpoints )
5434 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5438 * extend a track using route finder
5440 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5442 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5443 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5447 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5448 vtl->current_track = track;
5449 vtl->route_finder_started = TRUE;
5451 if ( track->trackpoints )
5452 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5458 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5460 // If have a vlp then perform a basic test to see if any DEM info available...
5462 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5464 if ( !g_list_length(dems) ) {
5465 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5473 * apply_dem_data_common:
5475 * A common function for applying the DEM values and reporting the results.
5477 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5479 if ( !trw_layer_dem_test ( vtl, vlp ) )
5482 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5483 // Inform user how much was changed
5485 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5486 g_snprintf(str, 64, tmp_str, changed);
5487 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5490 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5492 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5494 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5495 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5497 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5500 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5503 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5505 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5507 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5508 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5510 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5513 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5519 * A common function for applying the elevation smoothing and reporting the results.
5521 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5523 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5524 // Inform user how much was changed
5526 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5527 g_snprintf(str, 64, tmp_str, changed);
5528 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5534 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5536 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5538 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5539 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5541 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5546 smooth_it ( vtl, track, FALSE );
5549 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5551 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5553 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5554 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5556 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5561 smooth_it ( vtl, track, TRUE );
5565 * Commonal helper function
5567 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5570 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5571 g_snprintf(str, 64, tmp_str, changed);
5572 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5575 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5577 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5578 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5580 if ( !trw_layer_dem_test ( vtl, vlp ) )
5584 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5586 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5588 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5592 GHashTableIter iter;
5593 gpointer key, value;
5595 g_hash_table_iter_init ( &iter, vtl->waypoints );
5596 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5597 VikWaypoint *wp = VIK_WAYPOINT(value);
5598 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5601 wp_changed_message ( vtl, changed );
5604 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5606 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5607 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5609 if ( !trw_layer_dem_test ( vtl, vlp ) )
5613 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5615 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5617 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5621 GHashTableIter iter;
5622 gpointer key, value;
5624 g_hash_table_iter_init ( &iter, vtl->waypoints );
5625 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5626 VikWaypoint *wp = VIK_WAYPOINT(value);
5627 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5630 wp_changed_message ( vtl, changed );
5633 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5635 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5637 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5638 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5640 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5644 if ( !track->trackpoints )
5646 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5649 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5651 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5653 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5654 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5656 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5661 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5664 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5667 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5669 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5671 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5672 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5674 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5679 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5682 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5685 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5687 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5689 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5690 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5692 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5697 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5700 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5704 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5706 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5708 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5710 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5711 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5713 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5715 if ( trk && trk->trackpoints )
5717 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5718 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5719 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5720 if ( values[MA_VLP] )
5721 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5723 vik_layer_emit_update ( VIK_LAYER(vtl) );
5728 * Refine the selected track/route with a routing engine.
5729 * The routing engine is selected by the user, when requestiong the job.
5731 static void trw_layer_route_refine ( menu_array_sublayer values )
5733 static gint last_engine = 0;
5734 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5737 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5738 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5740 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5742 if ( trk && trk->trackpoints )
5744 /* Check size of the route */
5745 int nb = vik_track_get_tp_count(trk);
5747 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5748 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5749 GTK_MESSAGE_WARNING,
5750 GTK_BUTTONS_OK_CANCEL,
5751 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5753 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5754 gtk_widget_destroy ( dialog );
5755 if (response != GTK_RESPONSE_OK )
5758 /* Select engine from dialog */
5759 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5760 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5761 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5763 GTK_RESPONSE_REJECT,
5765 GTK_RESPONSE_ACCEPT,
5767 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5768 gtk_widget_show_all(label);
5770 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5772 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5773 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5774 gtk_widget_show_all(combo);
5776 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5778 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5780 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5782 /* Dialog validated: retrieve selected engine and do the job */
5783 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5784 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5787 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5789 /* Force saving track */
5790 /* FIXME: remove or rename this hack */
5791 vtl->route_finder_check_added_track = TRUE;
5794 vik_routing_engine_refine (routing, vtl, trk);
5796 /* FIXME: remove or rename this hack */
5797 if ( vtl->route_finder_added_track )
5798 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5800 vtl->route_finder_added_track = NULL;
5801 vtl->route_finder_check_added_track = FALSE;
5803 vik_layer_emit_update ( VIK_LAYER(vtl) );
5805 /* Restore cursor */
5806 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5808 gtk_widget_destroy ( dialog );
5812 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5814 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5815 trw_layer_tpwin_init ( vtl );
5818 /*************************************
5819 * merge/split by time routines
5820 *************************************/
5822 /* called for each key in track hash table.
5823 * If the current track has the same time stamp type, add it to the result,
5824 * except the one pointed by "exclude".
5825 * set exclude to NULL if there is no exclude to check.
5826 * Note that the result is in reverse (for performance reasons).
5831 gboolean with_timestamps;
5833 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5835 twt_udata *user_data = udata;
5836 VikTrackpoint *p1, *p2;
5837 VikTrack *trk = VIK_TRACK(value);
5838 if (trk == user_data->exclude) {
5842 if (trk->trackpoints) {
5843 p1 = vik_track_get_tp_first(trk);
5844 p2 = vik_track_get_tp_last(trk);
5846 if ( user_data->with_timestamps ) {
5847 if (!p1->has_timestamp || !p2->has_timestamp) {
5852 // Don't add tracks with timestamps when getting non timestamp tracks
5853 if (p1->has_timestamp || p2->has_timestamp) {
5859 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5863 * find_nearby_tracks_by_time:
5865 * Called for each track in track hash table.
5866 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5867 * to the current track, then the current track is added to the list in user_data[0]
5869 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5871 VikTrack *trk = VIK_TRACK(value);
5873 GList **nearby_tracks = ((gpointer *)user_data)[0];
5874 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5876 if ( !orig_trk || !orig_trk->trackpoints )
5880 * detect reasons for not merging, and return
5881 * if no reason is found not to merge, then do it.
5884 twt_udata *udata = user_data;
5885 // Exclude the original track from the compiled list
5886 if (trk == udata->exclude) {
5890 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5891 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5893 if (trk->trackpoints) {
5895 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5896 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5898 if (!p1->has_timestamp || !p2->has_timestamp) {
5899 //g_print("no timestamp\n");
5903 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5904 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5905 if (! (labs(t1 - p2->timestamp) < threshold ||
5907 labs(p1->timestamp - t2) < threshold)
5914 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5917 /* comparison function used to sort tracks; a and b are hash table keys */
5918 /* Not actively used - can be restored if needed
5919 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5921 GHashTable *tracks = user_data;
5924 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5925 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5927 if (t1 < t2) return -1;
5928 if (t1 > t2) return 1;
5933 /* comparison function used to sort trackpoints */
5934 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5936 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5938 if (t1 < t2) return -1;
5939 if (t1 > t2) return 1;
5944 * comparison function which can be used to sort tracks or waypoints by name
5946 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5948 const gchar* namea = (const gchar*) a;
5949 const gchar* nameb = (const gchar*) b;
5950 if ( namea == NULL || nameb == NULL)
5953 // Same sort method as used in the vik_treeview_*_alphabetize functions
5954 return strcmp ( namea, nameb );
5958 * Attempt to merge selected track with other tracks specified by the user
5959 * Tracks to merge with must be of the same 'type' as the selected track -
5960 * either all with timestamps, or all without timestamps
5962 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5964 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5965 GList *other_tracks = NULL;
5966 GHashTable *ght_tracks;
5967 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5968 ght_tracks = vtl->routes;
5970 ght_tracks = vtl->tracks;
5972 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5977 if ( !track->trackpoints )
5981 udata.result = &other_tracks;
5982 udata.exclude = track;
5983 // Allow merging with 'similar' time type time tracks
5984 // i.e. either those times, or those without
5985 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5987 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5988 other_tracks = g_list_reverse(other_tracks);
5990 if ( !other_tracks ) {
5991 if ( udata.with_timestamps )
5992 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5994 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5998 // Sort alphabetically for user presentation
5999 // Convert into list of names for usage with dialog function
6000 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6001 GList *other_tracks_names = NULL;
6002 GList *iter = g_list_first ( other_tracks );
6004 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
6005 iter = g_list_next ( iter );
6008 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
6010 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6014 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
6015 g_list_free(other_tracks);
6016 g_list_free(other_tracks_names);
6021 for (l = merge_list; l != NULL; l = g_list_next(l)) {
6022 VikTrack *merge_track;
6023 if ( track->is_route )
6024 merge_track = vik_trw_layer_get_route ( vtl, l->data );
6026 merge_track = vik_trw_layer_get_track ( vtl, l->data );
6029 vik_track_steal_and_append_trackpoints ( track, merge_track );
6030 if ( track->is_route )
6031 vik_trw_layer_delete_route (vtl, merge_track);
6033 vik_trw_layer_delete_track (vtl, merge_track);
6034 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
6037 for (l = merge_list; l != NULL; l = g_list_next(l))
6039 g_list_free(merge_list);
6041 vik_layer_emit_update( VIK_LAYER(vtl) );
6045 // c.f. trw_layer_sorted_track_id_by_name_list
6046 // but don't add the specified track to the list (normally current track)
6047 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
6049 twt_udata *user_data = udata;
6052 if (trk == user_data->exclude) {
6056 // Sort named list alphabetically
6057 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
6061 * Join - this allows combining 'tracks' and 'track routes'
6062 * i.e. doesn't care about whether tracks have consistent timestamps
6063 * ATM can only append one track at a time to the currently selected track
6065 static void trw_layer_append_track ( menu_array_sublayer values )
6068 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6070 GHashTable *ght_tracks;
6071 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6072 ght_tracks = vtl->routes;
6074 ght_tracks = vtl->tracks;
6076 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
6081 GList *other_tracks_names = NULL;
6083 // Sort alphabetically for user presentation
6084 // Convert into list of names for usage with dialog function
6085 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6087 udata.result = &other_tracks_names;
6088 udata.exclude = trk;
6090 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6092 // Note the limit to selecting one track only
6093 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6094 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6095 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6098 trk->is_route ? _("Append Route"): _("Append Track"),
6099 trk->is_route ? _("Select the route to append after the current route") :
6100 _("Select the track to append after the current track") );
6102 g_list_free(other_tracks_names);
6104 // It's a list, but shouldn't contain more than one other track!
6105 if ( append_list ) {
6107 for (l = append_list; l != NULL; l = g_list_next(l)) {
6108 // TODO: at present this uses the first track found by name,
6109 // which with potential multiple same named tracks may not be the one selected...
6110 VikTrack *append_track;
6111 if ( trk->is_route )
6112 append_track = vik_trw_layer_get_route ( vtl, l->data );
6114 append_track = vik_trw_layer_get_track ( vtl, l->data );
6116 if ( append_track ) {
6117 vik_track_steal_and_append_trackpoints ( trk, append_track );
6118 if ( trk->is_route )
6119 vik_trw_layer_delete_route (vtl, append_track);
6121 vik_trw_layer_delete_track (vtl, append_track);
6124 for (l = append_list; l != NULL; l = g_list_next(l))
6126 g_list_free(append_list);
6128 vik_layer_emit_update( VIK_LAYER(vtl) );
6133 * Very similar to trw_layer_append_track for joining
6134 * but this allows selection from the 'other' list
6135 * If a track is selected, then is shows routes and joins the selected one
6136 * If a route is selected, then is shows tracks and joins the selected one
6138 static void trw_layer_append_other ( menu_array_sublayer values )
6141 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6143 GHashTable *ght_mykind, *ght_others;
6144 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6145 ght_mykind = vtl->routes;
6146 ght_others = vtl->tracks;
6149 ght_mykind = vtl->tracks;
6150 ght_others = vtl->routes;
6153 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6158 GList *other_tracks_names = NULL;
6160 // Sort alphabetically for user presentation
6161 // Convert into list of names for usage with dialog function
6162 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6164 udata.result = &other_tracks_names;
6165 udata.exclude = trk;
6167 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6169 // Note the limit to selecting one track only
6170 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6171 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6172 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6175 trk->is_route ? _("Append Track"): _("Append Route"),
6176 trk->is_route ? _("Select the track to append after the current route") :
6177 _("Select the route to append after the current track") );
6179 g_list_free(other_tracks_names);
6181 // It's a list, but shouldn't contain more than one other track!
6182 if ( append_list ) {
6184 for (l = append_list; l != NULL; l = g_list_next(l)) {
6185 // TODO: at present this uses the first track found by name,
6186 // which with potential multiple same named tracks may not be the one selected...
6188 // Get FROM THE OTHER TYPE list
6189 VikTrack *append_track;
6190 if ( trk->is_route )
6191 append_track = vik_trw_layer_get_track ( vtl, l->data );
6193 append_track = vik_trw_layer_get_route ( vtl, l->data );
6195 if ( append_track ) {
6197 if ( !append_track->is_route &&
6198 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6199 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6201 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6202 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6203 vik_track_merge_segments ( append_track );
6204 vik_track_to_routepoints ( append_track );
6211 vik_track_steal_and_append_trackpoints ( trk, append_track );
6213 // Delete copied which is FROM THE OTHER TYPE list
6214 if ( trk->is_route )
6215 vik_trw_layer_delete_track (vtl, append_track);
6217 vik_trw_layer_delete_route (vtl, append_track);
6220 for (l = append_list; l != NULL; l = g_list_next(l))
6222 g_list_free(append_list);
6223 vik_layer_emit_update( VIK_LAYER(vtl) );
6227 /* merge by segments */
6228 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6230 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6231 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6232 guint segments = vik_track_merge_segments ( trk );
6233 // NB currently no need to redraw as segments not actually shown on the display
6234 // However inform the user of what happened:
6236 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6237 g_snprintf(str, 64, tmp_str, segments);
6238 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6241 /* merge by time routine */
6242 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6244 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6248 GList *tracks_with_timestamp = NULL;
6249 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6250 if (orig_trk->trackpoints &&
6251 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6252 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6257 udata.result = &tracks_with_timestamp;
6258 udata.exclude = orig_trk;
6259 udata.with_timestamps = TRUE;
6260 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6261 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6263 if (!tracks_with_timestamp) {
6264 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6267 g_list_free(tracks_with_timestamp);
6269 static guint threshold_in_minutes = 1;
6270 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6271 _("Merge Threshold..."),
6272 _("Merge when time between tracks less than:"),
6273 &threshold_in_minutes)) {
6277 // keep attempting to merge all tracks until no merges within the time specified is possible
6278 gboolean attempt_merge = TRUE;
6279 GList *nearby_tracks = NULL;
6281 static gpointer params[3];
6283 while ( attempt_merge ) {
6285 // Don't try again unless tracks have changed
6286 attempt_merge = FALSE;
6288 trps = orig_trk->trackpoints;
6292 if (nearby_tracks) {
6293 g_list_free(nearby_tracks);
6294 nearby_tracks = NULL;
6297 params[0] = &nearby_tracks;
6298 params[1] = orig_trk;
6299 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6301 /* get a list of adjacent-in-time tracks */
6302 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6305 GList *l = nearby_tracks;
6307 /* remove trackpoints from merged track, delete track */
6308 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6309 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6311 // Tracks have changed, therefore retry again against all the remaining tracks
6312 attempt_merge = TRUE;
6317 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6320 g_list_free(nearby_tracks);
6322 vik_layer_emit_update( VIK_LAYER(vtl) );
6326 * Split a track at the currently selected trackpoint
6328 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6330 if ( !vtl->current_tpl )
6333 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6334 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6336 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6337 GList *newglist = g_list_alloc ();
6338 newglist->prev = NULL;
6339 newglist->next = vtl->current_tpl->next;
6340 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6341 tr->trackpoints = newglist;
6343 vtl->current_tpl->next->prev = newglist; /* end old track here */
6344 vtl->current_tpl->next = NULL;
6346 // Bounds of the selected track changed due to the split
6347 vik_track_calculate_bounds ( vtl->current_tp_track );
6349 vtl->current_tpl = newglist; /* change tp to first of new track. */
6350 vtl->current_tp_track = tr;
6353 vik_trw_layer_add_route ( vtl, name, tr );
6355 vik_trw_layer_add_track ( vtl, name, tr );
6357 // Bounds of the new track created by the split
6358 vik_track_calculate_bounds ( tr );
6364 // Also need id of newly created track
6367 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6369 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6371 if ( trkf && udata.uuid )
6372 vtl->current_tp_id = udata.uuid;
6374 vtl->current_tp_id = NULL;
6376 vik_layer_emit_update(VIK_LAYER(vtl));
6382 /* split by time routine */
6383 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6385 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6386 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6387 GList *trps = track->trackpoints;
6389 GList *newlists = NULL;
6390 GList *newtps = NULL;
6391 static guint thr = 1;
6398 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6399 _("Split Threshold..."),
6400 _("Split when time between trackpoints exceeds:"),
6405 /* iterate through trackpoints, and copy them into new lists without touching original list */
6406 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6410 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6412 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6415 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6416 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6417 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6419 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6424 if (ts - prev_ts > thr*60) {
6425 /* flush accumulated trackpoints into new list */
6426 newlists = g_list_append(newlists, g_list_reverse(newtps));
6430 /* accumulate trackpoint copies in newtps, in reverse order */
6431 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6433 iter = g_list_next(iter);
6436 newlists = g_list_append(newlists, g_list_reverse(newtps));
6439 /* put lists of trackpoints into tracks */
6441 // Only bother updating if the split results in new tracks
6442 if (g_list_length (newlists) > 1) {
6447 tr = vik_track_copy ( track, FALSE );
6448 tr->trackpoints = (GList *)(iter->data);
6450 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6451 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6452 g_free ( new_tr_name );
6453 vik_track_calculate_bounds ( tr );
6454 iter = g_list_next(iter);
6456 // Remove original track and then update the display
6457 vik_trw_layer_delete_track (vtl, track);
6458 vik_layer_emit_update(VIK_LAYER(vtl));
6460 g_list_free(newlists);
6464 * Split a track by the number of points as specified by the user
6466 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6468 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6470 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6471 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6473 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6478 // Check valid track
6479 GList *trps = track->trackpoints;
6483 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6484 _("Split Every Nth Point"),
6485 _("Split on every Nth point:"),
6486 250, // Default value as per typical limited track capacity of various GPS devices
6490 // Was a valid number returned?
6496 GList *newlists = NULL;
6497 GList *newtps = NULL;
6502 /* accumulate trackpoint copies in newtps, in reverse order */
6503 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6505 if (count >= points) {
6506 /* flush accumulated trackpoints into new list */
6507 newlists = g_list_append(newlists, g_list_reverse(newtps));
6511 iter = g_list_next(iter);
6514 // If there is a remaining chunk put that into the new split list
6515 // This may well be the whole track if no split points were encountered
6517 newlists = g_list_append(newlists, g_list_reverse(newtps));
6520 /* put lists of trackpoints into tracks */
6522 // Only bother updating if the split results in new tracks
6523 if (g_list_length (newlists) > 1) {
6528 tr = vik_track_copy ( track, FALSE );
6529 tr->trackpoints = (GList *)(iter->data);
6531 if ( track->is_route ) {
6532 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6533 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6536 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6537 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6539 g_free ( new_tr_name );
6540 vik_track_calculate_bounds ( tr );
6542 iter = g_list_next(iter);
6544 // Remove original track and then update the display
6545 if ( track->is_route )
6546 vik_trw_layer_delete_route (vtl, track);
6548 vik_trw_layer_delete_track (vtl, track);
6549 vik_layer_emit_update(VIK_LAYER(vtl));
6551 g_list_free(newlists);
6555 * Split a track at the currently selected trackpoint
6557 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6559 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6560 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6561 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6565 * Split a track by its segments
6566 * Routes do not have segments so don't call this for routes
6568 static void trw_layer_split_segments ( menu_array_sublayer values )
6570 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6571 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6578 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6581 for ( i = 0; i < ntracks; i++ ) {
6583 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6584 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6585 g_free ( new_tr_name );
6590 // Remove original track
6591 vik_trw_layer_delete_track ( vtl, trk );
6592 vik_layer_emit_update ( VIK_LAYER(vtl) );
6595 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6598 /* end of split/merge routines */
6600 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6604 // Find available adjacent trackpoint
6605 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6606 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6607 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6609 // Delete current trackpoint
6610 vik_trackpoint_free ( vtl->current_tpl->data );
6611 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6613 // Set to current to the available adjacent trackpoint
6614 vtl->current_tpl = new_tpl;
6616 if ( vtl->current_tp_track ) {
6617 vik_track_calculate_bounds ( vtl->current_tp_track );
6621 // Delete current trackpoint
6622 vik_trackpoint_free ( vtl->current_tpl->data );
6623 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6624 trw_layer_cancel_current_tp ( vtl, FALSE );
6629 * Delete the selected point
6631 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6633 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6635 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6636 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6638 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6643 if ( !vtl->current_tpl )
6646 trw_layer_trackpoint_selected_delete ( vtl, trk );
6648 // Track has been updated so update tps:
6649 trw_layer_cancel_tps_of_track ( vtl, trk );
6651 vik_layer_emit_update ( VIK_LAYER(vtl) );
6655 * Delete adjacent track points at the same position
6656 * AKA Delete Dulplicates on the Properties Window
6658 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6660 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6662 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6663 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6665 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6670 gulong removed = vik_track_remove_dup_points ( trk );
6672 // Track has been updated so update tps:
6673 trw_layer_cancel_tps_of_track ( vtl, trk );
6675 // Inform user how much was deleted as it's not obvious from the normal view
6677 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6678 g_snprintf(str, 64, tmp_str, removed);
6679 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6681 vik_layer_emit_update ( VIK_LAYER(vtl) );
6685 * Delete adjacent track points with the same timestamp
6686 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6688 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6690 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6692 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6693 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6695 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6700 gulong removed = vik_track_remove_same_time_points ( trk );
6702 // Track has been updated so update tps:
6703 trw_layer_cancel_tps_of_track ( vtl, trk );
6705 // Inform user how much was deleted as it's not obvious from the normal view
6707 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6708 g_snprintf(str, 64, tmp_str, removed);
6709 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6711 vik_layer_emit_update ( VIK_LAYER(vtl) );
6717 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6719 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6721 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6722 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6724 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6729 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6731 vik_layer_emit_update ( VIK_LAYER(vtl) );
6734 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6736 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6738 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6739 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6741 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6746 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6748 vik_layer_emit_update ( VIK_LAYER(vtl) );
6754 static void trw_layer_reverse ( menu_array_sublayer values )
6756 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6758 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6759 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6761 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6766 vik_track_reverse ( track );
6768 vik_layer_emit_update ( VIK_LAYER(vtl) );
6772 * Open a program at the specified date
6773 * Mainly for RedNotebook - http://rednotebook.sourceforge.net/
6774 * But could work with any program that accepts a command line of --date=<date>
6775 * FUTURE: Allow configuring of command line options + date format
6777 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6780 gchar *cmd = g_strdup_printf ( "%s %s%s", diary_program, "--date=", date_str );
6781 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6782 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), diary_program );
6783 g_error_free ( err );
6789 * Open a diary at the date of the track or waypoint
6791 static void trw_layer_diary ( menu_array_sublayer values )
6793 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6795 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6796 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6802 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6803 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6804 trw_layer_diary_open ( vtl, date_buf );
6807 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6809 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6810 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6816 if ( wpt->has_timestamp ) {
6817 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6818 trw_layer_diary_open ( vtl, date_buf );
6821 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6826 * Open a program at the specified date
6827 * Mainly for Stellarium - http://stellarium.org/
6828 * But could work with any program that accepts the same command line options...
6829 * FUTURE: Allow configuring of command line options + format or parameters
6831 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 )
6835 gint fd = g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, &err );
6837 g_warning ( "%s: Failed to open temporary file: %s", __FUNCTION__, err->message );
6838 g_clear_error ( &err );
6841 gchar *cmd = g_strdup_printf ( "%s %s %s %s %s %s %s %s %s %s %s %s %s %s",
6842 astro_program, "-c", tmp, "--full-screen no", "--sky-date", date_str, "--sky-time", time_str, "--latitude", lat_str, "--longitude", lon_str, "--altitude", alt_str );
6843 g_warning ( "%s", cmd );
6844 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6845 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s"), astro_program );
6846 g_warning ( "%s", err->message );
6847 g_error_free ( err );
6849 util_add_to_deletion_list ( tmp );
6854 // Format of stellarium lat & lon seems designed to be particularly awkward
6855 // who uses ' & " in the parameters for the command line?!
6858 static gchar *convert_to_dms ( gdouble dec )
6864 gchar *result = NULL;
6878 tmp = (tmp - val_d) * 60;
6882 val_s = (tmp - val_m) * 60;
6885 result = g_strdup_printf ( "%c%dd%d\\\'%.4f\\\"", sign_c, val_d, val_m, val_s );
6890 * Open an astronomy program at the date & position of the track center, trackpoint or waypoint
6892 static void trw_layer_astro ( menu_array_sublayer values )
6894 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6896 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6897 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6901 VikTrackpoint *tp = NULL;
6902 if ( vtl->current_tpl )
6903 // Current Trackpoint
6904 tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6905 else if ( trk->trackpoints )
6906 // Otherwise first trackpoint
6907 tp = VIK_TRACKPOINT(trk->trackpoints->data);
6912 if ( tp->has_timestamp ) {
6914 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(tp->timestamp)));
6916 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(tp->timestamp)));
6918 vik_coord_to_latlon ( &tp->coord, &ll );
6919 gchar *lat_str = convert_to_dms ( ll.lat );
6920 gchar *lon_str = convert_to_dms ( ll.lon );
6922 snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(tp->altitude) );
6923 trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf);
6928 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6930 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6931 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6935 if ( wpt->has_timestamp ) {
6937 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(wpt->timestamp)));
6939 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(wpt->timestamp)));
6941 vik_coord_to_latlon ( &wpt->coord, &ll );
6942 gchar *lat_str = convert_to_dms ( ll.lat );
6943 gchar *lon_str = convert_to_dms ( ll.lon );
6945 snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(wpt->altitude) );
6946 trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf );
6951 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6956 * Similar to trw_layer_enum_item, but this uses a sorted method
6959 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6961 GList **list = (GList**)udata;
6962 // *list = g_list_prepend(*all, key); //unsorted method
6963 // Sort named list alphabetically
6964 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6969 * Now Waypoint specific sort
6971 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6973 GList **list = (GList**)udata;
6974 // Sort named list alphabetically
6975 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6979 * Track specific sort
6981 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6983 GList **list = (GList**)udata;
6984 // Sort named list alphabetically
6985 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6990 gboolean has_same_track_name;
6991 const gchar *same_track_name;
6992 } same_track_name_udata;
6994 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6996 const gchar* namea = (const gchar*) aa;
6997 const gchar* nameb = (const gchar*) bb;
7000 gint result = strcmp ( namea, nameb );
7002 if ( result == 0 ) {
7003 // Found two names the same
7004 same_track_name_udata *user_data = udata;
7005 user_data->has_same_track_name = TRUE;
7006 user_data->same_track_name = namea;
7009 // Leave ordering the same
7014 * Find out if any tracks have the same name in this hash table
7016 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
7018 // Sort items by name, then compare if any next to each other are the same
7020 GList *track_names = NULL;
7021 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7024 if ( ! track_names )
7027 same_track_name_udata udata;
7028 udata.has_same_track_name = FALSE;
7030 // Use sort routine to traverse list comparing items
7031 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7032 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7033 // Still no tracks...
7037 return udata.has_same_track_name;
7041 * Force unqiue track names for the track table specified
7042 * Note the panel is a required parameter to enable the update of the names displayed
7043 * Specify if on tracks or else on routes
7045 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
7047 // . Search list for an instance of repeated name
7048 // . get track of this name
7049 // . create new name
7050 // . rename track & update equiv. treeview iter
7051 // . repeat until all different
7053 same_track_name_udata udata;
7055 GList *track_names = NULL;
7056 udata.has_same_track_name = FALSE;
7057 udata.same_track_name = NULL;
7059 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7062 if ( ! track_names )
7065 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7067 // Still no tracks...
7068 if ( ! dummy_list1 )
7071 while ( udata.has_same_track_name ) {
7073 // Find a track with the same name
7076 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
7078 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
7082 g_critical("Houston, we've had a problem.");
7083 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7084 _("Internal Error in vik_trw_layer_uniquify_tracks") );
7089 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
7090 vik_track_set_name ( trk, newname );
7096 // Need want key of it for treeview update
7097 gpointer trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7099 if ( trkf && udataU.uuid ) {
7103 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7105 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7108 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
7110 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
7112 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
7117 // Start trying to find same names again...
7119 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7120 udata.has_same_track_name = FALSE;
7121 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7123 // No tracks any more - give up searching
7124 if ( ! dummy_list2 )
7125 udata.has_same_track_name = FALSE;
7129 vik_layers_panel_emit_update ( vlp );
7132 static void trw_layer_sort_order_specified ( VikTrwLayer *vtl, guint sublayer_type, vik_layer_sort_order_t order )
7136 switch (sublayer_type) {
7137 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
7138 iter = &(vtl->tracks_iter);
7139 vtl->track_sort_order = order;
7141 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
7142 iter = &(vtl->routes_iter);
7143 vtl->track_sort_order = order;
7145 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
7146 iter = &(vtl->waypoints_iter);
7147 vtl->wp_sort_order = order;
7151 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, order );
7154 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
7156 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7157 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_ASCENDING );
7160 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
7162 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7163 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_DESCENDING );
7166 static void trw_layer_sort_order_timestamp_ascend ( menu_array_sublayer values )
7168 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7169 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_ASCENDING );
7172 static void trw_layer_sort_order_timestamp_descend ( menu_array_sublayer values )
7174 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7175 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_DESCENDING );
7181 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
7183 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7186 // Ensure list of track names offered is unique
7187 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
7188 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7189 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7190 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
7196 // Sort list alphabetically for better presentation
7197 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7200 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
7204 // Get list of items to delete from the user
7205 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7208 _("Delete Selection"),
7209 _("Select tracks to delete"));
7212 // Delete requested tracks
7213 // since specificly requested, IMHO no need for extra confirmation
7214 if ( delete_list ) {
7216 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7217 // This deletes first trk it finds of that name (but uniqueness is enforced above)
7218 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
7220 g_list_free(delete_list);
7221 // Reset layer timestamps in case they have now changed
7222 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
7224 vik_layer_emit_update( VIK_LAYER(vtl) );
7231 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
7233 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7236 // Ensure list of track names offered is unique
7237 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
7238 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7239 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7240 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
7246 // Sort list alphabetically for better presentation
7247 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7250 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
7254 // Get list of items to delete from the user
7255 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7258 _("Delete Selection"),
7259 _("Select routes to delete") );
7262 // Delete requested routes
7263 // since specificly requested, IMHO no need for extra confirmation
7264 if ( delete_list ) {
7266 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7267 // This deletes first route it finds of that name (but uniqueness is enforced above)
7268 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
7270 g_list_free(delete_list);
7271 vik_layer_emit_update( VIK_LAYER(vtl) );
7276 gboolean has_same_waypoint_name;
7277 const gchar *same_waypoint_name;
7278 } same_waypoint_name_udata;
7280 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
7282 const gchar* namea = (const gchar*) aa;
7283 const gchar* nameb = (const gchar*) bb;
7286 gint result = strcmp ( namea, nameb );
7288 if ( result == 0 ) {
7289 // Found two names the same
7290 same_waypoint_name_udata *user_data = udata;
7291 user_data->has_same_waypoint_name = TRUE;
7292 user_data->same_waypoint_name = namea;
7295 // Leave ordering the same
7300 * Find out if any waypoints have the same name in this layer
7302 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
7304 // Sort items by name, then compare if any next to each other are the same
7306 GList *waypoint_names = NULL;
7307 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7310 if ( ! waypoint_names )
7313 same_waypoint_name_udata udata;
7314 udata.has_same_waypoint_name = FALSE;
7316 // Use sort routine to traverse list comparing items
7317 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7318 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7319 // Still no waypoints...
7323 return udata.has_same_waypoint_name;
7327 * Force unqiue waypoint names for this layer
7328 * Note the panel is a required parameter to enable the update of the names displayed
7330 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7332 // . Search list for an instance of repeated name
7333 // . get waypoint of this name
7334 // . create new name
7335 // . rename waypoint & update equiv. treeview iter
7336 // . repeat until all different
7338 same_waypoint_name_udata udata;
7340 GList *waypoint_names = NULL;
7341 udata.has_same_waypoint_name = FALSE;
7342 udata.same_waypoint_name = NULL;
7344 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7347 if ( ! waypoint_names )
7350 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7352 // Still no waypoints...
7353 if ( ! dummy_list1 )
7356 while ( udata.has_same_waypoint_name ) {
7358 // Find a waypoint with the same name
7359 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7363 g_critical("Houston, we've had a problem.");
7364 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7365 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7370 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7372 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7376 // Start trying to find same names again...
7377 waypoint_names = NULL;
7378 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7379 udata.has_same_waypoint_name = FALSE;
7380 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7382 // No waypoints any more - give up searching
7383 if ( ! dummy_list2 )
7384 udata.has_same_waypoint_name = FALSE;
7388 vik_layers_panel_emit_update ( vlp );
7394 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7396 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7399 // Ensure list of waypoint names offered is unique
7400 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7401 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7402 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7403 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7409 // Sort list alphabetically for better presentation
7410 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7412 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7416 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7418 // Get list of items to delete from the user
7419 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7422 _("Delete Selection"),
7423 _("Select waypoints to delete"));
7426 // Delete requested waypoints
7427 // since specificly requested, IMHO no need for extra confirmation
7428 if ( delete_list ) {
7430 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7431 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7432 trw_layer_delete_waypoint_by_name (vtl, l->data);
7434 g_list_free(delete_list);
7436 trw_layer_calculate_bounds_waypoints ( vtl );
7437 // Reset layer timestamp in case it has now changed
7438 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
7439 vik_layer_emit_update( VIK_LAYER(vtl) );
7447 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7449 vik_treeview_item_toggle_visible ( vt, it );
7455 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7457 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7463 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7465 wp->visible = GPOINTER_TO_INT (on_off);
7471 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7473 wp->visible = !wp->visible;
7479 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7481 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7482 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7483 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7484 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7486 vik_layer_emit_update ( VIK_LAYER(vtl) );
7492 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7494 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7495 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7496 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7497 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7499 vik_layer_emit_update ( VIK_LAYER(vtl) );
7505 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7507 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7508 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7509 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7511 vik_layer_emit_update ( VIK_LAYER(vtl) );
7517 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7519 trk->visible = GPOINTER_TO_INT (on_off);
7525 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7527 trk->visible = !trk->visible;
7533 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7535 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7536 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7537 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7538 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7540 vik_layer_emit_update ( VIK_LAYER(vtl) );
7546 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7548 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7549 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7550 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7551 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7553 vik_layer_emit_update ( VIK_LAYER(vtl) );
7559 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7561 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7562 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7563 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7565 vik_layer_emit_update ( VIK_LAYER(vtl) );
7571 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7573 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7574 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7575 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7576 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7578 vik_layer_emit_update ( VIK_LAYER(vtl) );
7584 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7586 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7587 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7588 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7589 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7591 vik_layer_emit_update ( VIK_LAYER(vtl) );
7597 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7599 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7600 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7601 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7603 vik_layer_emit_update ( VIK_LAYER(vtl) );
7607 * vik_trw_layer_build_waypoint_list_t:
7609 * Helper function to construct a list of #vik_trw_waypoint_list_t
7611 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7613 GList *waypoints_and_layers = NULL;
7614 // build waypoints_and_layers list
7615 while ( waypoints ) {
7616 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7617 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7619 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7620 waypoints = g_list_next ( waypoints );
7622 return waypoints_and_layers;
7626 * trw_layer_create_waypoint_list:
7628 * Create the latest list of waypoints with the associated layer(s)
7629 * Although this will always be from a single layer here
7631 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7633 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7634 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7636 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7640 * trw_layer_analyse_close:
7642 * Stuff to do on dialog closure
7644 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7646 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7647 gtk_widget_destroy ( dialog );
7648 vtl->tracks_analysis_dialog = NULL;
7652 * vik_trw_layer_build_track_list_t:
7654 * Helper function to construct a list of #vik_trw_track_list_t
7656 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7658 GList *tracks_and_layers = NULL;
7659 // build tracks_and_layers list
7661 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7662 vtdl->trk = VIK_TRACK(tracks->data);
7664 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7665 tracks = g_list_next ( tracks );
7667 return tracks_and_layers;
7671 * trw_layer_create_track_list:
7673 * Create the latest list of tracks with the associated layer(s)
7674 * Although this will always be from a single layer here
7676 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7678 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7679 GList *tracks = NULL;
7680 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7681 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7683 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7685 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7688 static void trw_layer_tracks_stats ( menu_array_layer values )
7690 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7691 // There can only be one!
7692 if ( vtl->tracks_analysis_dialog )
7695 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7696 VIK_LAYER(vtl)->name,
7698 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7699 trw_layer_create_track_list,
7700 trw_layer_analyse_close );
7706 static void trw_layer_routes_stats ( menu_array_layer values )
7708 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7709 // There can only be one!
7710 if ( vtl->tracks_analysis_dialog )
7713 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7714 VIK_LAYER(vtl)->name,
7716 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7717 trw_layer_create_track_list,
7718 trw_layer_analyse_close );
7721 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7723 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7724 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7726 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7729 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7731 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7732 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7735 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7736 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7740 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7742 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7743 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7747 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7748 } else if ( !strncmp(wp->comment, "http", 4) ) {
7749 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7750 } else if ( !strncmp(wp->description, "http", 4) ) {
7751 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7755 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7757 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7759 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7761 // No actual change to the name supplied
7763 if (strcmp(newname, wp->name) == 0 )
7766 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7769 // An existing waypoint has been found with the requested name
7770 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7771 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7776 // Update WP name and refresh the treeview
7777 vik_waypoint_set_name (wp, newname);
7779 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7780 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7782 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7787 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7789 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7791 // No actual change to the name supplied
7793 if (strcmp(newname, trk->name) == 0)
7796 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7799 // An existing track has been found with the requested name
7800 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7801 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7805 // Update track name and refresh GUI parts
7806 vik_track_set_name (trk, newname);
7808 // Update any subwindows that could be displaying this track which has changed name
7809 // Only one Track Edit Window
7810 if ( l->current_tp_track == trk && l->tpwin ) {
7811 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7813 // Property Dialog of the track
7814 vik_trw_layer_propwin_update ( trk );
7816 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7817 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7819 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7824 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7826 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7828 // No actual change to the name supplied
7830 if (strcmp(newname, trk->name) == 0)
7833 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7836 // An existing track has been found with the requested name
7837 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7838 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7842 // Update track name and refresh GUI parts
7843 vik_track_set_name (trk, newname);
7845 // Update any subwindows that could be displaying this track which has changed name
7846 // Only one Track Edit Window
7847 if ( l->current_tp_track == trk && l->tpwin ) {
7848 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7850 // Property Dialog of the track
7851 vik_trw_layer_propwin_update ( trk );
7853 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7854 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7856 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7863 static gboolean is_valid_geocache_name ( gchar *str )
7865 gint len = strlen ( str );
7866 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]));
7870 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7872 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7873 a_acquire_set_filter_track ( trk );
7877 #ifdef VIK_CONFIG_GOOGLE
7878 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7880 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7881 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7884 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7886 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7888 gchar *escaped = g_uri_escape_string ( tr->comment, NULL, TRUE );
7889 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7890 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7897 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7898 /* viewpoint is now available instead */
7899 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7901 static menu_array_sublayer pass_along;
7903 gboolean rv = FALSE;
7905 pass_along[MA_VTL] = l;
7906 pass_along[MA_VLP] = vlp;
7907 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7908 pass_along[MA_SUBLAYER_ID] = sublayer;
7909 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7910 pass_along[MA_VVP] = vvp;
7911 pass_along[MA_TV_ITER] = iter;
7912 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7914 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7918 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7919 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7920 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7921 gtk_widget_show ( item );
7923 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7924 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7925 if (tr && tr->property_dialog)
7926 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7928 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7929 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7930 if (tr && tr->property_dialog)
7931 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7934 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7936 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7937 gtk_widget_show ( item );
7939 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7941 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7942 gtk_widget_show ( item );
7944 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7946 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7947 gtk_widget_show ( item );
7949 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7951 // Always create separator as now there is always at least the transform menu option
7952 item = gtk_menu_item_new ();
7953 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7954 gtk_widget_show ( item );
7956 /* could be a right-click using the tool */
7957 if ( vlp != NULL ) {
7958 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7959 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7960 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7961 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7962 gtk_widget_show ( item );
7965 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7967 if ( wp && wp->name ) {
7968 if ( is_valid_geocache_name ( wp->name ) ) {
7969 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7970 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7971 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7972 gtk_widget_show ( item );
7974 #ifdef VIK_CONFIG_GEOTAG
7975 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7976 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7977 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7978 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7979 gtk_widget_show ( item );
7983 if ( wp && wp->image )
7985 // Set up image paramater
7986 pass_along[MA_MISC] = wp->image;
7988 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7989 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
7990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7991 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7992 gtk_widget_show ( item );
7994 #ifdef VIK_CONFIG_GEOTAG
7995 GtkWidget *geotag_submenu = gtk_menu_new ();
7996 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7997 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7998 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7999 gtk_widget_show ( item );
8000 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
8002 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
8003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
8004 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
8005 gtk_widget_show ( item );
8007 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
8008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
8009 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
8010 gtk_widget_show ( item );
8017 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
8018 ( wp->description && !strncmp(wp->description, "http", 4) )) {
8019 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
8020 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8021 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
8022 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8023 gtk_widget_show ( item );
8029 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
8030 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
8031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
8032 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8033 gtk_widget_show ( item );
8034 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
8035 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
8036 gtk_widget_set_sensitive ( item, TRUE );
8038 gtk_widget_set_sensitive ( item, FALSE );
8041 item = gtk_menu_item_new ();
8042 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8043 gtk_widget_show ( item );
8046 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
8049 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
8050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
8052 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8053 gtk_widget_show ( item );
8056 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
8058 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
8059 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
8061 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8062 gtk_widget_show ( item );
8064 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
8065 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
8067 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8068 gtk_widget_show ( item );
8070 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
8071 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
8073 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8074 gtk_widget_show ( item );
8076 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
8077 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
8079 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8080 gtk_widget_show ( item );
8082 GtkWidget *vis_submenu = gtk_menu_new ();
8083 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8084 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8085 gtk_widget_show ( item );
8086 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8088 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
8089 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8090 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
8091 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8092 gtk_widget_show ( item );
8094 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
8095 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8096 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
8097 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8098 gtk_widget_show ( item );
8100 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8101 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8102 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
8103 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8104 gtk_widget_show ( item );
8106 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
8107 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
8109 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8112 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
8116 if ( l->current_track && !l->current_track->is_route ) {
8117 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
8118 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8119 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8120 gtk_widget_show ( item );
8122 item = gtk_menu_item_new ();
8123 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8124 gtk_widget_show ( item );
8127 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
8128 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8129 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
8130 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8131 gtk_widget_show ( item );
8133 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
8134 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
8136 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8137 gtk_widget_show ( item );
8138 // Make it available only when a new track *not* already in progress
8139 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8141 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
8142 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8143 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
8144 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8145 gtk_widget_show ( item );
8147 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
8148 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8149 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
8150 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8151 gtk_widget_show ( item );
8153 GtkWidget *vis_submenu = gtk_menu_new ();
8154 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8155 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8156 gtk_widget_show ( item );
8157 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8159 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
8160 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8161 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
8162 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8163 gtk_widget_show ( item );
8165 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
8166 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8167 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
8168 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8169 gtk_widget_show ( item );
8171 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8172 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
8174 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8176 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
8177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8179 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8180 gtk_widget_show ( item );
8182 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8183 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
8184 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8185 gtk_widget_show ( item );
8188 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
8192 if ( l->current_track && l->current_track->is_route ) {
8193 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8194 // Reuse finish track method
8195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8196 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8197 gtk_widget_show ( item );
8199 item = gtk_menu_item_new ();
8200 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8201 gtk_widget_show ( item );
8204 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
8205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
8207 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8208 gtk_widget_show ( item );
8210 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
8211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8212 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
8213 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8214 gtk_widget_show ( item );
8215 // Make it available only when a new track *not* already in progress
8216 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8218 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
8219 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
8221 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8222 gtk_widget_show ( item );
8224 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
8225 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8226 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
8227 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8228 gtk_widget_show ( item );
8230 GtkWidget *vis_submenu = gtk_menu_new ();
8231 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8232 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8233 gtk_widget_show ( item );
8234 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8236 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
8237 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8238 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
8239 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8240 gtk_widget_show ( item );
8242 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
8243 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8244 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
8245 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8246 gtk_widget_show ( item );
8248 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8249 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8250 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
8251 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8253 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
8254 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8258 gtk_widget_show ( item );
8260 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8261 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
8262 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8263 gtk_widget_show ( item );
8267 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
8268 GtkWidget *submenu_sort = gtk_menu_new ();
8269 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
8270 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8271 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8272 gtk_widget_show ( item );
8273 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
8275 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
8276 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8277 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
8278 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8279 gtk_widget_show ( item );
8281 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
8282 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8283 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
8284 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8285 gtk_widget_show ( item );
8287 item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") );
8288 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8289 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_ascend), pass_along );
8290 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8291 gtk_widget_show ( item );
8293 item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") );
8294 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_descend), pass_along );
8296 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8297 gtk_widget_show ( item );
8300 GtkWidget *upload_submenu = gtk_menu_new ();
8302 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8304 item = gtk_menu_item_new ();
8305 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8306 gtk_widget_show ( item );
8308 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
8309 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
8310 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
8311 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8312 if ( l->current_track ) {
8313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8314 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8315 gtk_widget_show ( item );
8318 item = gtk_menu_item_new ();
8319 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8320 gtk_widget_show ( item );
8323 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8324 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
8326 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
8327 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8328 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
8329 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8330 gtk_widget_show ( item );
8332 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8333 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
8334 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8335 gtk_widget_show ( item );
8337 GtkWidget *goto_submenu;
8338 goto_submenu = gtk_menu_new ();
8339 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
8340 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8341 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8342 gtk_widget_show ( item );
8343 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8345 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8346 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8347 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8348 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8349 gtk_widget_show ( item );
8351 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8352 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8353 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8354 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8355 gtk_widget_show ( item );
8357 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8358 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8359 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8360 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8361 gtk_widget_show ( item );
8363 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8364 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8365 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8366 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8367 gtk_widget_show ( item );
8369 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8370 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8372 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8373 gtk_widget_show ( item );
8375 // Routes don't have speeds
8376 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8377 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8378 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8380 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8381 gtk_widget_show ( item );
8384 GtkWidget *combine_submenu;
8385 combine_submenu = gtk_menu_new ();
8386 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8387 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8388 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8389 gtk_widget_show ( item );
8390 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8392 // Routes don't have times or segments...
8393 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8394 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8395 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8396 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8397 gtk_widget_show ( item );
8399 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8400 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8401 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8402 gtk_widget_show ( item );
8405 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8406 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8407 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8408 gtk_widget_show ( item );
8410 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8411 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8413 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8414 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8415 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8416 gtk_widget_show ( item );
8418 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8419 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8421 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8423 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8424 gtk_widget_show ( item );
8426 GtkWidget *split_submenu;
8427 split_submenu = gtk_menu_new ();
8428 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8429 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8430 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8431 gtk_widget_show ( item );
8432 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8434 // Routes don't have times or segments...
8435 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8436 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8438 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8439 gtk_widget_show ( item );
8441 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8442 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8443 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8444 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8445 gtk_widget_show ( item );
8448 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8449 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8450 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8451 gtk_widget_show ( item );
8453 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8455 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8456 gtk_widget_show ( item );
8457 // Make it available only when a trackpoint is selected.
8458 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8460 GtkWidget *insert_submenu = gtk_menu_new ();
8461 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8462 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8463 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8464 gtk_widget_show ( item );
8465 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8467 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8468 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8469 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8470 gtk_widget_show ( item );
8471 // Make it available only when a point is selected
8472 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8474 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8475 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8476 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8477 gtk_widget_show ( item );
8478 // Make it available only when a point is selected
8479 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8481 GtkWidget *delete_submenu;
8482 delete_submenu = gtk_menu_new ();
8483 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8484 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8485 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8486 gtk_widget_show ( item );
8487 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8489 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8490 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8491 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8492 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8493 gtk_widget_show ( item );
8494 // Make it available only when a point is selected
8495 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8497 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8499 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8500 gtk_widget_show ( item );
8502 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8503 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8504 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8505 gtk_widget_show ( item );
8507 GtkWidget *transform_submenu;
8508 transform_submenu = gtk_menu_new ();
8509 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8510 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8511 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8512 gtk_widget_show ( item );
8513 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8515 GtkWidget *dem_submenu;
8516 dem_submenu = gtk_menu_new ();
8517 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8518 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
8519 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8520 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8522 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8524 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8525 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8526 gtk_widget_show ( item );
8528 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8530 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8531 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8532 gtk_widget_show ( item );
8534 GtkWidget *smooth_submenu;
8535 smooth_submenu = gtk_menu_new ();
8536 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8537 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8538 gtk_widget_show ( item );
8539 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8541 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8542 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8543 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8544 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8545 gtk_widget_show ( item );
8547 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8548 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8549 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8550 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8551 gtk_widget_show ( item );
8553 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8554 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8556 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8557 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8558 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8559 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8560 gtk_widget_show ( item );
8562 // Routes don't have timestamps - so these are only available for tracks
8563 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8564 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8566 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8567 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8568 gtk_widget_show ( item );
8570 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolate Times") );
8571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_interpolate_times), pass_along );
8572 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8573 gtk_widget_set_tooltip_text (item, _("Reset trackpoint timestamps between the first and last points such that track is traveled at equal speed"));
8574 gtk_widget_show ( item );
8577 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8578 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8580 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8581 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8582 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8583 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8584 gtk_widget_show ( item );
8586 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8587 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8588 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8590 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8591 gtk_widget_show ( item );
8594 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8596 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8597 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8599 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8600 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
8601 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8602 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8603 gtk_widget_show ( item );
8606 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8607 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8609 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8610 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8612 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8613 gtk_widget_show ( item );
8615 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8616 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8618 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8619 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8620 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8621 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8622 gtk_widget_show ( item );
8624 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8625 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8626 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
8627 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8628 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8629 gtk_widget_show ( item );
8632 // ATM can't upload a single waypoint but can do waypoints to a GPS
8633 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8634 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8635 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8636 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8637 gtk_widget_show ( item );
8638 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8640 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8641 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8642 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8643 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8644 gtk_widget_show ( item );
8648 GtkWidget *external_submenu = create_external_submenu ( menu );
8650 // These are only made available if a suitable program is installed
8651 if ( (have_astro_program || have_diary_program) &&
8652 (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) {
8654 if ( have_diary_program ) {
8655 item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") );
8656 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8657 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8658 gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8659 gtk_widget_set_tooltip_text (item, _("Open diary program at this date"));
8660 gtk_widget_show ( item );
8663 if ( have_astro_program ) {
8664 item = gtk_image_menu_item_new_with_mnemonic ( _("_Astronomy") );
8665 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_astro), pass_along );
8666 gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8667 gtk_widget_set_tooltip_text (item, _("Open astronomy program at this date and location"));
8668 gtk_widget_show ( item );
8672 if ( l->current_tpl || l->current_wp ) {
8673 // For the selected point
8675 if ( l->current_tpl )
8676 vc = &(VIK_TRACKPOINT(l->current_tpl->data)->coord);
8678 vc = &(l->current_wp->coord);
8679 vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), vc );
8682 // Otherwise for the selected sublayer
8683 // TODO: Should use selected items centre - rather than implicitly using the current viewport
8684 vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), NULL );
8688 #ifdef VIK_CONFIG_GOOGLE
8689 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8691 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8692 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8693 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8694 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8695 gtk_widget_show ( item );
8699 // Some things aren't usable with routes
8700 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8701 #ifdef VIK_CONFIG_OPENSTREETMAP
8702 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8703 // Convert internal pointer into track
8704 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8705 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8706 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8707 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8708 gtk_widget_show ( item );
8711 // Currently filter with functions all use shellcommands and thus don't work in Windows
8713 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8716 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8717 gtk_widget_show ( item );
8720 /* ATM This function is only available via the layers panel, due to needing a vlp */
8722 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8723 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8724 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8726 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8727 gtk_widget_show ( item );
8731 #ifdef VIK_CONFIG_GEOTAG
8732 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8733 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8734 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8735 gtk_widget_show ( item );
8739 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8740 // Only show on viewport popmenu when a trackpoint is selected
8741 if ( ! vlp && l->current_tpl ) {
8743 item = gtk_menu_item_new ();
8744 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8745 gtk_widget_show ( item );
8747 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8748 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8749 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8750 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8751 gtk_widget_show ( item );
8755 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8756 GtkWidget *transform_submenu;
8757 transform_submenu = gtk_menu_new ();
8758 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8759 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8760 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8761 gtk_widget_show ( item );
8762 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8764 GtkWidget *dem_submenu;
8765 dem_submenu = gtk_menu_new ();
8766 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8767 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
8768 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8769 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8771 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8772 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8773 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8774 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8775 gtk_widget_show ( item );
8777 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8778 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8779 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8780 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8781 gtk_widget_show ( item );
8784 gtk_widget_show_all ( GTK_WIDGET(menu) );
8789 // TODO: Probably better to rework this track manipulation in viktrack.c
8790 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8793 if (!vtl->current_tpl)
8796 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8797 VikTrackpoint *tp_other = NULL;
8800 if (!vtl->current_tpl->prev)
8802 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8804 if (!vtl->current_tpl->next)
8806 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8809 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8812 VikTrackpoint *tp_new = vik_trackpoint_new();
8813 struct LatLon ll_current, ll_other;
8814 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8815 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8817 /* main positional interpolation */
8818 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8819 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8821 /* Now other properties that can be interpolated */
8822 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8824 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8825 /* Note here the division is applied to each part, then added
8826 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8827 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8828 tp_new->has_timestamp = TRUE;
8831 if (tp_current->speed != NAN && tp_other->speed != NAN)
8832 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8834 /* TODO - improve interpolation of course, as it may not be correct.
8835 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8836 [similar applies if value is in radians] */
8837 if (tp_current->course != NAN && tp_other->course != NAN)
8838 tp_new->course = (tp_current->course + tp_other->course)/2;
8840 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8842 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8843 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8845 // Otherwise try routes
8846 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8850 gint index = g_list_index ( trk->trackpoints, tp_current );
8854 // NB no recalculation of bounds since it is inserted between points
8855 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8860 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8866 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8870 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8872 if ( vtl->current_tpl )
8874 vtl->current_tpl = NULL;
8875 vtl->current_tp_track = NULL;
8876 vtl->current_tp_id = NULL;
8877 vik_layer_emit_update(VIK_LAYER(vtl));
8881 static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8883 VikTrack *trk = vtl->current_tp_track;
8885 // Notional center of a track is simply an average of the bounding box extremities
8886 struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8887 vik_coord_load_from_latlon ( &vc, vtl->coord_mode, ¢er );
8888 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8891 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8893 g_assert ( vtl->tpwin != NULL );
8894 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8895 trw_layer_cancel_current_tp ( vtl, TRUE );
8897 if ( vtl->current_tpl == NULL )
8900 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8902 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8903 my_tpwin_set_tp ( vtl );
8905 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8907 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8909 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8913 trw_layer_trackpoint_selected_delete ( vtl, tr );
8915 if ( vtl->current_tpl )
8916 // Reset dialog with the available adjacent trackpoint
8917 my_tpwin_set_tp ( vtl );
8919 vik_layer_emit_update(VIK_LAYER(vtl));
8921 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8923 if ( vtl->current_tp_track ) {
8924 vtl->current_tpl = vtl->current_tpl->next;
8925 my_tpwin_set_tp ( vtl );
8927 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8929 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8931 if ( vtl->current_tp_track ) {
8932 vtl->current_tpl = vtl->current_tpl->prev;
8933 my_tpwin_set_tp ( vtl );
8935 vik_layer_emit_update(VIK_LAYER(vtl));
8937 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8939 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8940 vik_layer_emit_update(VIK_LAYER(vtl));
8942 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8943 vik_layer_emit_update(VIK_LAYER(vtl));
8947 * trw_layer_dialog_shift:
8948 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8950 * Try to reposition a dialog if it's over the specified coord
8951 * so to not obscure the item of interest
8953 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8955 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8957 // Attempt force dialog to be shown so we can find out where it is more reliably...
8958 while ( gtk_events_pending() )
8959 gtk_main_iteration ();
8961 // get parent window position & size
8962 gint win_pos_x, win_pos_y;
8963 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8965 gint win_size_x, win_size_y;
8966 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8968 // get own dialog size
8969 gint dia_size_x, dia_size_y;
8970 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8972 // get own dialog position
8973 gint dia_pos_x, dia_pos_y;
8974 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8976 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8977 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8979 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8981 gint vp_xx, vp_yy; // In viewport pixels
8982 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8984 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8988 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8990 // Transform Viewport pixels into absolute pixels
8991 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8992 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8994 // Is dialog over the point (to within an ^^ edge value)
8995 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8996 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
9000 gint hh = vik_viewport_get_height ( vvp );
9002 // Consider the difference in viewport to the full window
9003 gint offset_y = dest_y;
9004 // Add difference between dialog and window sizes
9005 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
9007 if ( vp_yy > hh/2 ) {
9008 // Point in bottom half, move window to top half
9009 gtk_window_move ( dialog, dia_pos_x, offset_y );
9012 // Point in top half, move dialog down
9013 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
9017 // Shift left<->right
9018 gint ww = vik_viewport_get_width ( vvp );
9020 // Consider the difference in viewport to the full window
9021 gint offset_x = dest_x;
9022 // Add difference between dialog and window sizes
9023 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
9025 if ( vp_xx > ww/2 ) {
9026 // Point on right, move window to left
9027 gtk_window_move ( dialog, offset_x, dia_pos_y );
9030 // Point on left, move right
9031 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
9039 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
9043 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9044 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
9045 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
9046 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
9048 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
9050 if ( vtl->current_tpl ) {
9051 // get tp pixel position
9052 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9054 // Shift up<->down to try not to obscure the trackpoint.
9055 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
9059 if ( vtl->current_tpl )
9060 if ( vtl->current_tp_track )
9061 my_tpwin_set_tp ( vtl );
9062 /* set layer name and TP data */
9065 /***************************************************************************
9067 ***************************************************************************/
9069 /*** Utility data structures and functions ****/
9073 gint closest_x, closest_y;
9074 gboolean draw_images;
9075 gpointer closest_wp_id;
9076 VikWaypoint *closest_wp;
9082 gint closest_x, closest_y;
9083 gpointer closest_track_id;
9084 VikTrackpoint *closest_tp;
9090 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
9096 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
9098 // If waypoint has an image then use the image size to select
9099 if ( params->draw_images && wp->image ) {
9100 gint slackx, slacky;
9101 slackx = wp->image_width / 2;
9102 slacky = wp->image_height / 2;
9104 if ( x <= params->x + slackx && x >= params->x - slackx
9105 && y <= params->y + slacky && y >= params->y - slacky ) {
9106 params->closest_wp_id = id;
9107 params->closest_wp = wp;
9108 params->closest_x = x;
9109 params->closest_y = y;
9112 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
9113 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
9114 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
9116 params->closest_wp_id = id;
9117 params->closest_wp = wp;
9118 params->closest_x = x;
9119 params->closest_y = y;
9123 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
9125 GList *tpl = t->trackpoints;
9131 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
9137 tp = VIK_TRACKPOINT(tpl->data);
9139 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
9141 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
9142 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
9143 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
9145 params->closest_track_id = id;
9146 params->closest_tp = tp;
9147 params->closest_tpl = tpl;
9148 params->closest_x = x;
9149 params->closest_y = y;
9155 // ATM: Leave this as 'Track' only.
9156 // Not overly bothered about having a snap to route trackpoint capability
9157 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
9159 TPSearchParams params;
9163 params.closest_track_id = NULL;
9164 params.closest_tp = NULL;
9165 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9166 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9167 return params.closest_tp;
9170 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
9172 WPSearchParams params;
9176 params.draw_images = vtl->drawimages;
9177 params.closest_wp = NULL;
9178 params.closest_wp_id = NULL;
9179 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9180 return params.closest_wp;
9184 // Some forward declarations
9185 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
9186 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
9187 static void marker_end_move ( tool_ed_t *t );
9190 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
9194 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9196 // Here always allow snapping back to the original location
9197 // this is useful when one decides not to move the thing afterall
9198 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
9201 if ( event->state & GDK_CONTROL_MASK )
9203 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9205 new_coord = tp->coord;
9209 if ( event->state & GDK_SHIFT_MASK )
9211 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9213 new_coord = wp->coord;
9217 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9219 marker_moveto ( t, x, y );
9226 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
9228 if ( t->holding && event->button == 1 )
9230 // Prevent accidental (small) shifts when specific movement has not been requested
9231 // (as the click release has occurred within the click object detection area)
9236 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9239 if ( event->state & GDK_CONTROL_MASK )
9241 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9243 new_coord = tp->coord;
9247 if ( event->state & GDK_SHIFT_MASK )
9249 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9251 new_coord = wp->coord;
9254 marker_end_move ( t );
9256 // Determine if working on a waypoint or a trackpoint
9257 if ( t->is_waypoint ) {
9258 // Update waypoint position
9259 vtl->current_wp->coord = new_coord;
9260 trw_layer_calculate_bounds_waypoints ( vtl );
9261 // Reset waypoint pointer
9262 vtl->current_wp = NULL;
9263 vtl->current_wp_id = NULL;
9266 if ( vtl->current_tpl ) {
9267 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9269 if ( vtl->current_tp_track )
9270 vik_track_calculate_bounds ( vtl->current_tp_track );
9273 if ( vtl->current_tp_track )
9274 my_tpwin_set_tp ( vtl );
9275 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
9279 vik_layer_emit_update ( VIK_LAYER(vtl) );
9286 Returns true if a waypoint or track is found near the requested event position for this particular layer
9287 The item found is automatically selected
9288 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
9290 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
9292 if ( event->button != 1 )
9295 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9298 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9302 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
9304 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
9306 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
9307 WPSearchParams wp_params;
9308 wp_params.vvp = vvp;
9309 wp_params.x = event->x;
9310 wp_params.y = event->y;
9311 wp_params.draw_images = vtl->drawimages;
9312 wp_params.closest_wp_id = NULL;
9313 wp_params.closest_wp = NULL;
9315 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
9317 if ( wp_params.closest_wp ) {
9320 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
9322 // Too easy to move it so must be holding shift to start immediately moving it
9323 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
9324 if ( event->state & GDK_SHIFT_MASK ||
9325 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
9326 // Put into 'move buffer'
9327 // NB vvp & vw already set in tet
9328 tet->vtl = (gpointer)vtl;
9329 tet->is_waypoint = TRUE;
9331 marker_begin_move (tet, event->x, event->y);
9334 vtl->current_wp = wp_params.closest_wp;
9335 vtl->current_wp_id = wp_params.closest_wp_id;
9337 if ( event->type == GDK_2BUTTON_PRESS ) {
9338 if ( vtl->current_wp->image ) {
9339 menu_array_sublayer values;
9340 values[MA_VTL] = vtl;
9341 values[MA_MISC] = vtl->current_wp->image;
9342 trw_layer_show_picture ( values );
9346 vik_layer_emit_update ( VIK_LAYER(vtl) );
9352 // Used for both track and route lists
9353 TPSearchParams tp_params;
9354 tp_params.vvp = vvp;
9355 tp_params.x = event->x;
9356 tp_params.y = event->y;
9357 tp_params.closest_track_id = NULL;
9358 tp_params.closest_tp = NULL;
9359 tp_params.closest_tpl = NULL;
9360 tp_params.bbox = bbox;
9362 if (vtl->tracks_visible) {
9363 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
9365 if ( tp_params.closest_tp ) {
9367 // Always select + highlight the track
9368 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
9370 tet->is_waypoint = FALSE;
9372 // Select the Trackpoint
9373 // Can move it immediately when control held or it's the previously selected tp
9374 if ( event->state & GDK_CONTROL_MASK ||
9375 vtl->current_tpl == tp_params.closest_tpl ) {
9376 // Put into 'move buffer'
9377 // NB vvp & vw already set in tet
9378 tet->vtl = (gpointer)vtl;
9379 marker_begin_move (tet, event->x, event->y);
9382 vtl->current_tpl = tp_params.closest_tpl;
9383 vtl->current_tp_id = tp_params.closest_track_id;
9384 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
9386 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9389 my_tpwin_set_tp ( vtl );
9391 vik_layer_emit_update ( VIK_LAYER(vtl) );
9396 // Try again for routes
9397 if (vtl->routes_visible) {
9398 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9400 if ( tp_params.closest_tp ) {
9402 // Always select + highlight the track
9403 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9405 tet->is_waypoint = FALSE;
9407 // Select the Trackpoint
9408 // Can move it immediately when control held or it's the previously selected tp
9409 if ( event->state & GDK_CONTROL_MASK ||
9410 vtl->current_tpl == tp_params.closest_tpl ) {
9411 // Put into 'move buffer'
9412 // NB vvp & vw already set in tet
9413 tet->vtl = (gpointer)vtl;
9414 marker_begin_move (tet, event->x, event->y);
9417 vtl->current_tpl = tp_params.closest_tpl;
9418 vtl->current_tp_id = tp_params.closest_track_id;
9419 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9421 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9424 my_tpwin_set_tp ( vtl );
9426 vik_layer_emit_update ( VIK_LAYER(vtl) );
9431 /* these aren't the droids you're looking for */
9432 vtl->current_wp = NULL;
9433 vtl->current_wp_id = NULL;
9434 trw_layer_cancel_current_tp ( vtl, FALSE );
9437 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9442 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9444 if ( event->button != 3 )
9447 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9450 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9453 /* Post menu for the currently selected item */
9455 /* See if a track is selected */
9456 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9457 if ( track && track->visible ) {
9459 if ( track->name ) {
9461 if ( vtl->track_right_click_menu )
9462 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9464 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9471 if ( track->is_route )
9472 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9474 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9476 if ( trkf && udataU.uuid ) {
9479 if ( track->is_route )
9480 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9482 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9484 trw_layer_sublayer_add_menu_items ( vtl,
9485 vtl->track_right_click_menu,
9487 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9493 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9499 /* See if a waypoint is selected */
9500 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9501 if ( waypoint && waypoint->visible ) {
9502 if ( waypoint->name ) {
9504 if ( vtl->wp_right_click_menu )
9505 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9507 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9510 udata.wp = waypoint;
9513 gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9515 if ( wpf && udata.uuid ) {
9516 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9518 trw_layer_sublayer_add_menu_items ( vtl,
9519 vtl->wp_right_click_menu,
9521 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9526 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9535 /* background drawing hook, to be passed the viewport */
9536 static gboolean tool_sync_done = TRUE;
9538 static gboolean tool_sync(gpointer data)
9540 VikViewport *vvp = data;
9541 vik_viewport_sync(vvp);
9542 tool_sync_done = TRUE;
9546 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9549 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9550 gdk_gc_set_function ( t->gc, GDK_INVERT );
9551 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9552 vik_viewport_sync(t->vvp);
9558 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9560 VikViewport *vvp = t->vvp;
9561 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9562 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9567 if (tool_sync_done) {
9568 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9569 tool_sync_done = FALSE;
9573 static void marker_end_move ( tool_ed_t *t )
9575 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9576 g_object_unref ( t->gc );
9581 /*** Edit waypoint ****/
9583 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9585 tool_ed_t *t = g_new(tool_ed_t, 1);
9591 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9596 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9598 WPSearchParams params;
9599 tool_ed_t *t = data;
9600 VikViewport *vvp = t->vvp;
9602 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9609 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9612 if ( vtl->current_wp && vtl->current_wp->visible )
9614 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9616 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9618 if ( abs(x - (int)round(event->x)) <= WAYPOINT_SIZE_APPROX &&
9619 abs(y - (int)round(event->y)) <= WAYPOINT_SIZE_APPROX )
9621 if ( event->button == 3 )
9622 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9624 marker_begin_move(t, event->x, event->y);
9631 params.x = event->x;
9632 params.y = event->y;
9633 params.draw_images = vtl->drawimages;
9634 params.closest_wp_id = NULL;
9635 params.closest_wp = NULL;
9636 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9637 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9639 if ( event->button == 3 )
9640 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9642 marker_begin_move(t, event->x, event->y);
9645 else if ( params.closest_wp )
9647 if ( event->button == 3 )
9648 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9650 vtl->waypoint_rightclick = FALSE;
9652 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9654 vtl->current_wp = params.closest_wp;
9655 vtl->current_wp_id = params.closest_wp_id;
9657 /* could make it so don't update if old WP is off screen and new is null but oh well */
9658 vik_layer_emit_update ( VIK_LAYER(vtl) );
9662 vtl->current_wp = NULL;
9663 vtl->current_wp_id = NULL;
9664 vtl->waypoint_rightclick = FALSE;
9665 vik_layer_emit_update ( VIK_LAYER(vtl) );
9669 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9671 tool_ed_t *t = data;
9672 VikViewport *vvp = t->vvp;
9674 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9679 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9682 if ( event->state & GDK_CONTROL_MASK )
9684 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9686 new_coord = tp->coord;
9690 if ( event->state & GDK_SHIFT_MASK )
9692 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9693 if ( wp && wp != vtl->current_wp )
9694 new_coord = wp->coord;
9699 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9701 marker_moveto ( t, x, y );
9708 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9710 tool_ed_t *t = data;
9711 VikViewport *vvp = t->vvp;
9713 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9716 if ( t->holding && event->button == 1 )
9719 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9722 if ( event->state & GDK_CONTROL_MASK )
9724 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9726 new_coord = tp->coord;
9730 if ( event->state & GDK_SHIFT_MASK )
9732 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9733 if ( wp && wp != vtl->current_wp )
9734 new_coord = wp->coord;
9737 marker_end_move ( t );
9739 vtl->current_wp->coord = new_coord;
9741 trw_layer_calculate_bounds_waypoints ( vtl );
9742 vik_layer_emit_update ( VIK_LAYER(vtl) );
9745 /* PUT IN RIGHT PLACE!!! */
9746 if ( event->button == 3 && vtl->waypoint_rightclick )
9748 if ( vtl->wp_right_click_menu )
9749 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9750 if ( vtl->current_wp ) {
9751 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9752 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 );
9753 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9755 vtl->waypoint_rightclick = FALSE;
9760 /*** New track ****/
9762 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9769 GdkDrawable *drawable;
9775 * Draw specified pixmap
9777 static gboolean draw_sync ( gpointer data )
9779 draw_sync_t *ds = (draw_sync_t*) data;
9780 // Sometimes don't want to draw
9781 // normally because another update has taken precedent such as panning the display
9782 // which means this pixmap is no longer valid
9783 if ( ds->vtl->draw_sync_do ) {
9784 gdk_draw_drawable (ds->drawable,
9787 0, 0, 0, 0, -1, -1);
9788 ds->vtl->draw_sync_done = TRUE;
9794 static gchar* distance_string (gdouble distance)
9798 /* draw label with distance */
9799 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9800 switch (dist_units) {
9801 case VIK_UNITS_DISTANCE_MILES:
9802 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9803 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9804 } else if (distance < 1609.4) {
9805 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9807 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9810 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
9811 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
9812 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
9813 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
9814 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9816 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
9820 // VIK_UNITS_DISTANCE_KILOMETRES
9821 if (distance >= 1000 && distance < 100000) {
9822 g_sprintf(str, "%3.2f km", distance/1000.0);
9823 } else if (distance < 1000) {
9824 g_sprintf(str, "%d m", (int)distance);
9826 g_sprintf(str, "%d km", (int)distance/1000);
9830 return g_strdup (str);
9834 * Actually set the message in statusbar
9836 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9838 // Only show elevation data when track has some elevation properties
9839 gchar str_gain_loss[64];
9840 str_gain_loss[0] = '\0';
9841 gchar str_last_step[64];
9842 str_last_step[0] = '\0';
9843 gchar *str_total = distance_string (distance);
9845 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9846 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9847 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9849 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9852 if ( last_step > 0 ) {
9853 gchar *tmp = distance_string (last_step);
9854 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9858 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9860 // Write with full gain/loss information
9861 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9862 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9864 g_free ( str_total );
9868 * Figure out what information should be set in the statusbar and then write it
9870 static void update_statusbar ( VikTrwLayer *vtl )
9872 // Get elevation data
9873 gdouble elev_gain, elev_loss;
9874 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9876 /* Find out actual distance of current track */
9877 gdouble distance = vik_track_get_length (vtl->current_track);
9879 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9883 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9885 /* if we haven't sync'ed yet, we don't have time to do more. */
9886 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9887 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9889 static GdkPixmap *pixmap = NULL;
9891 // Need to check in case window has been resized
9892 w1 = vik_viewport_get_width(vvp);
9893 h1 = vik_viewport_get_height(vvp);
9895 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9897 gdk_drawable_get_size (pixmap, &w2, &h2);
9898 if (w1 != w2 || h1 != h2) {
9899 g_object_unref ( G_OBJECT ( pixmap ) );
9900 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9903 // Reset to background
9904 gdk_draw_drawable (pixmap,
9905 vtl->current_track_newpoint_gc,
9906 vik_viewport_get_pixmap(vvp),
9907 0, 0, 0, 0, -1, -1);
9909 draw_sync_t *passalong;
9912 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9914 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9915 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9916 // thus when we come to reset to the background it would include what we have already drawn!!
9917 gdk_draw_line ( pixmap,
9918 vtl->current_track_newpoint_gc,
9919 x1, y1, event->x, event->y );
9920 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9922 /* Find out actual distance of current track */
9923 gdouble distance = vik_track_get_length (vtl->current_track);
9925 // Now add distance to where the pointer is //
9928 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9929 vik_coord_to_latlon ( &coord, &ll );
9930 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9931 distance = distance + last_step;
9933 // Get elevation data
9934 gdouble elev_gain, elev_loss;
9935 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9937 // Adjust elevation data (if available) for the current pointer position
9939 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9940 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9941 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9942 // Adjust elevation of last track point
9943 if ( elev_new > last_tpt->altitude )
9945 elev_gain += elev_new - last_tpt->altitude;
9948 elev_loss += last_tpt->altitude - elev_new;
9953 // Display of the distance 'tooltip' during track creation is controlled by a preference
9955 if ( a_vik_get_create_track_tooltip() ) {
9957 gchar *str = distance_string (distance);
9959 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9960 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9961 pango_layout_set_text (pl, str, -1);
9963 pango_layout_get_pixel_size ( pl, &wd, &hd );
9966 // offset from cursor a bit depending on font size
9970 // Create a background block to make the text easier to read over the background map
9971 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9972 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9973 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9975 g_object_unref ( G_OBJECT ( pl ) );
9976 g_object_unref ( G_OBJECT ( background_block_gc ) );
9980 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9981 passalong->vtl = vtl;
9982 passalong->pixmap = pixmap;
9983 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9984 passalong->gc = vtl->current_track_newpoint_gc;
9988 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9990 // Update statusbar with full gain/loss information
9991 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9993 // draw pixmap when we have time to
9994 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9995 vtl->draw_sync_done = FALSE;
9996 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9998 return VIK_LAYER_TOOL_ACK;
10001 // NB vtl->current_track must be valid
10002 static void undo_trackpoint_add ( VikTrwLayer *vtl )
10005 if ( vtl->current_track->trackpoints ) {
10006 // TODO rework this...
10007 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
10008 GList *last = g_list_last(vtl->current_track->trackpoints);
10009 g_free ( last->data );
10010 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
10012 vik_track_calculate_bounds ( vtl->current_track );
10016 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10018 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10019 // Bin track if only one point as it's not very useful
10020 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
10021 if ( vtl->current_track->is_route )
10022 vik_trw_layer_delete_route ( vtl, vtl->current_track );
10024 vik_trw_layer_delete_track ( vtl, vtl->current_track );
10026 vtl->current_track = NULL;
10027 vik_layer_emit_update ( VIK_LAYER(vtl) );
10029 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10030 undo_trackpoint_add ( vtl );
10031 update_statusbar ( vtl );
10032 vik_layer_emit_update ( VIK_LAYER(vtl) );
10039 * Common function to handle trackpoint button requests on either a route or a track
10040 * . enables adding a point via normal click
10041 * . enables removal of last point via right click
10042 * . finishing of the track or route via double clicking
10044 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10048 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10051 if ( event->button == 2 ) {
10052 // As the display is panning, the new track pixmap is now invalid so don't draw it
10053 // otherwise this drawing done results in flickering back to an old image
10054 vtl->draw_sync_do = FALSE;
10058 if ( event->button == 3 )
10060 if ( !vtl->current_track )
10062 undo_trackpoint_add ( vtl );
10063 update_statusbar ( vtl );
10064 vik_layer_emit_update ( VIK_LAYER(vtl) );
10068 if ( event->type == GDK_2BUTTON_PRESS )
10070 /* subtract last (duplicate from double click) tp then end */
10071 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
10073 /* undo last, then end */
10074 undo_trackpoint_add ( vtl );
10075 vtl->current_track = NULL;
10077 vik_layer_emit_update ( VIK_LAYER(vtl) );
10081 tp = vik_trackpoint_new();
10082 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
10084 /* snap to other TP */
10085 if ( event->state & GDK_CONTROL_MASK )
10087 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10089 tp->coord = other_tp->coord;
10092 tp->newsegment = FALSE;
10093 tp->has_timestamp = FALSE;
10096 if ( vtl->current_track ) {
10097 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
10098 /* Auto attempt to get elevation from DEM data (if it's available) */
10099 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
10102 vtl->ct_x1 = vtl->ct_x2;
10103 vtl->ct_y1 = vtl->ct_y2;
10104 vtl->ct_x2 = event->x;
10105 vtl->ct_y2 = event->y;
10107 vik_layer_emit_update ( VIK_LAYER(vtl) );
10111 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10113 // if we were running the route finder, cancel it
10114 vtl->route_finder_started = FALSE;
10116 // ----------------------------------------------------- if current is a route - switch to new track
10117 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
10119 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
10120 if ( a_vik_get_ask_for_create_track_name() ) {
10121 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
10125 new_track_create_common ( vtl, name );
10128 return tool_new_track_or_route_click ( vtl, event, vvp );
10131 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10133 if ( event->button == 2 ) {
10134 // Pan moving ended - enable potential point drawing again
10135 vtl->draw_sync_do = TRUE;
10136 vtl->draw_sync_done = TRUE;
10140 /*** New route ****/
10142 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
10147 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10149 // if we were running the route finder, cancel it
10150 vtl->route_finder_started = FALSE;
10152 // -------------------------- if current is a track - switch to new route,
10153 if ( event->button == 1 && ( ! vtl->current_track ||
10154 (vtl->current_track && !vtl->current_track->is_route ) ) )
10156 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
10157 if ( a_vik_get_ask_for_create_track_name() ) {
10158 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
10162 new_route_create_common ( vtl, name );
10165 return tool_new_track_or_route_click ( vtl, event, vvp );
10168 /*** New waypoint ****/
10170 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
10175 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10178 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10180 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
10181 if ( vik_trw_layer_new_waypoint (vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord) ) {
10182 trw_layer_calculate_bounds_waypoints ( vtl );
10183 if ( VIK_LAYER(vtl)->visible )
10184 vik_layer_emit_update ( VIK_LAYER(vtl) );
10190 /*** Edit trackpoint ****/
10192 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
10194 tool_ed_t *t = g_new(tool_ed_t, 1);
10196 t->holding = FALSE;
10200 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
10206 * tool_edit_trackpoint_click:
10208 * On 'initial' click: search for the nearest trackpoint or routepoint and store it as the current trackpoint
10209 * Then update the viewport, statusbar and edit dialog to draw the point as being selected and it's information.
10210 * On subsequent clicks: (as the current trackpoint is defined) and the click is very near the same point
10211 * then initiate the move operation to drag the point to a new destination.
10212 * NB The current trackpoint will get reset elsewhere.
10214 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10216 tool_ed_t *t = data;
10217 VikViewport *vvp = t->vvp;
10218 TPSearchParams params;
10220 params.x = event->x;
10221 params.y = event->y;
10222 params.closest_track_id = NULL;
10223 params.closest_tp = NULL;
10224 params.closest_tpl = NULL;
10225 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
10227 if ( event->button != 1 )
10230 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10233 if ( !vtl->vl.visible || !(vtl->tracks_visible || vtl->routes_visible) )
10236 if ( vtl->current_tpl )
10238 /* 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.) */
10239 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
10240 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
10242 current_tr = VIK_TRACK(g_hash_table_lookup(vtl->routes, vtl->current_tp_id));
10247 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
10249 if ( current_tr->visible &&
10250 abs(x - (int)round(event->x)) < TRACKPOINT_SIZE_APPROX &&
10251 abs(y - (int)round(event->y)) < TRACKPOINT_SIZE_APPROX ) {
10252 marker_begin_move ( t, event->x, event->y );
10258 if ( vtl->tracks_visible )
10259 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
10261 if ( params.closest_tp )
10263 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
10264 vtl->current_tpl = params.closest_tpl;
10265 vtl->current_tp_id = params.closest_track_id;
10266 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
10267 trw_layer_tpwin_init ( vtl );
10268 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
10269 vik_layer_emit_update ( VIK_LAYER(vtl) );
10273 if ( vtl->routes_visible )
10274 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
10276 if ( params.closest_tp )
10278 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
10279 vtl->current_tpl = params.closest_tpl;
10280 vtl->current_tp_id = params.closest_track_id;
10281 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
10282 trw_layer_tpwin_init ( vtl );
10283 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
10284 vik_layer_emit_update ( VIK_LAYER(vtl) );
10288 /* these aren't the droids you're looking for */
10292 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
10294 tool_ed_t *t = data;
10295 VikViewport *vvp = t->vvp;
10297 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10302 VikCoord new_coord;
10303 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10306 if ( event->state & GDK_CONTROL_MASK )
10308 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10309 if ( tp && tp != vtl->current_tpl->data )
10310 new_coord = tp->coord;
10312 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10315 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
10316 marker_moveto ( t, x, y );
10324 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10326 tool_ed_t *t = data;
10327 VikViewport *vvp = t->vvp;
10329 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10331 if ( event->button != 1)
10334 if ( t->holding ) {
10335 VikCoord new_coord;
10336 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10339 if ( event->state & GDK_CONTROL_MASK )
10341 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10342 if ( tp && tp != vtl->current_tpl->data )
10343 new_coord = tp->coord;
10346 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10347 if ( vtl->current_tp_track )
10348 vik_track_calculate_bounds ( vtl->current_tp_track );
10350 marker_end_move ( t );
10352 /* diff dist is diff from orig */
10354 if ( vtl->current_tp_track )
10355 my_tpwin_set_tp ( vtl );
10357 vik_layer_emit_update ( VIK_LAYER(vtl) );
10364 /*** Extended Route Finder ***/
10366 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
10371 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10374 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10376 g_free ( new_end );
10377 vik_layer_emit_update ( VIK_LAYER(vtl) );
10379 /* remove last ' to:...' */
10380 if ( vtl->current_track->comment ) {
10381 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
10382 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
10383 gchar *new_comment = g_strndup ( vtl->current_track->comment,
10384 last_to - vtl->current_track->comment - 1);
10385 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
10392 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10395 if ( !vtl ) return FALSE;
10396 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
10397 if ( event->button == 3 && vtl->current_track ) {
10398 tool_extended_route_finder_undo ( vtl );
10400 else if ( event->button == 2 ) {
10401 vtl->draw_sync_do = FALSE;
10404 // if we started the track but via undo deleted all the track points, begin again
10405 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10406 return tool_new_track_or_route_click ( vtl, event, vvp );
10408 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10409 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
10410 struct LatLon start, end;
10412 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10413 vik_coord_to_latlon ( &(tp_start->coord), &start );
10414 vik_coord_to_latlon ( &(tmp), &end );
10416 vtl->route_finder_started = TRUE;
10417 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10419 // update UI to let user know what's going on
10420 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10421 VikRoutingEngine *engine = vik_routing_default_engine ( );
10423 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10426 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10427 vik_routing_engine_get_label ( engine ),
10428 start.lat, start.lon, end.lat, end.lon );
10429 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10431 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10434 /* Give GTK a change to display the new status bar before querying the web */
10435 while ( gtk_events_pending ( ) )
10436 gtk_main_iteration ( );
10438 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10440 /* Update UI to say we're done */
10441 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10442 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10443 vik_routing_engine_get_label ( engine ),
10444 start.lat, start.lon, end.lat, end.lon )
10445 : g_strdup_printf ( _("Error getting route from %s."),
10446 vik_routing_engine_get_label ( engine ) );
10447 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10450 vik_layer_emit_update ( VIK_LAYER(vtl) );
10452 vtl->current_track = NULL;
10454 // create a new route where we will add the planned route to
10455 gboolean ret = tool_new_route_click( vtl, event, vvp );
10457 vtl->route_finder_started = TRUE;
10464 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10466 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10467 vtl->route_finder_started = FALSE;
10468 vtl->current_track = NULL;
10469 vik_layer_emit_update ( VIK_LAYER(vtl) );
10471 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10472 tool_extended_route_finder_undo ( vtl );
10479 /*** Show picture ****/
10481 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10486 /* Params are: vvp, event, last match found or NULL */
10487 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10489 if ( wp->image && wp->visible )
10491 gint x, y, slackx, slacky;
10492 GdkEventButton *event = (GdkEventButton *) params[1];
10494 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10495 slackx = wp->image_width / 2;
10496 slacky = wp->image_height / 2;
10497 if ( x <= event->x + slackx && x >= event->x - slackx
10498 && y <= event->y + slacky && y >= event->y - slacky )
10500 params[2] = wp->image; /* we've found a match. however continue searching
10501 * since we want to find the last match -- that
10502 * is, the match that was drawn last. */
10507 static void trw_layer_show_picture ( menu_array_sublayer values )
10509 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10511 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10512 #else /* WINDOWS */
10513 GError *err = NULL;
10514 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10515 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10516 g_free ( quoted_file );
10517 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10519 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() );
10520 g_error_free ( err );
10523 #endif /* WINDOWS */
10526 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10528 gpointer params[3] = { vvp, event, NULL };
10529 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10531 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10534 static menu_array_sublayer values;
10535 values[MA_VTL] = vtl;
10536 values[MA_MISC] = params[2];
10537 trw_layer_show_picture ( values );
10538 return TRUE; /* found a match */
10541 return FALSE; /* go through other layers, searching for a match */
10544 /***************************************************************************
10546 ***************************************************************************/
10549 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10551 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10552 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10555 /* Structure for thumbnail creating data used in the background thread */
10557 VikTrwLayer *vtl; // Layer needed for redrawing
10558 GSList *pics; // Image list
10559 } thumbnail_create_thread_data;
10561 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10563 guint total = g_slist_length(tctd->pics), done = 0;
10564 while ( tctd->pics )
10566 a_thumbnails_create ( (gchar *) tctd->pics->data );
10567 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10569 return -1; /* Abort thread */
10571 tctd->pics = tctd->pics->next;
10574 // Redraw to show the thumbnails as they are now created
10575 if ( IS_VIK_LAYER(tctd->vtl) )
10576 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10581 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10583 while ( tctd->pics )
10585 g_free ( tctd->pics->data );
10586 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10591 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl )
10593 if ( ! vtl->has_verified_thumbnails )
10595 GSList *pics = NULL;
10596 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10599 gint len = g_slist_length ( pics );
10600 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10601 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10604 a_background_thread ( BACKGROUND_POOL_LOCAL,
10605 VIK_GTK_WINDOW_FROM_LAYER(vtl),
10607 (vik_thr_func) create_thumbnails_thread,
10609 (vik_thr_free_func) thumbnail_create_thread_free,
10617 static const gchar* my_track_colors ( gint ii )
10619 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10631 // Fast and reliable way of returning a colour
10632 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10635 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10637 GHashTableIter iter;
10638 gpointer key, value;
10642 g_hash_table_iter_init ( &iter, vtl->tracks );
10644 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10646 // Tracks get a random spread of colours if not already assigned
10647 if ( ! VIK_TRACK(value)->has_color ) {
10648 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10649 VIK_TRACK(value)->color = vtl->track_color;
10651 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10653 VIK_TRACK(value)->has_color = TRUE;
10656 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10659 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10665 g_hash_table_iter_init ( &iter, vtl->routes );
10667 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10669 // Routes get an intermix of reds
10670 if ( ! VIK_TRACK(value)->has_color ) {
10672 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10674 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10675 VIK_TRACK(value)->has_color = TRUE;
10678 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10685 * (Re)Calculate the bounds of the waypoints in this layer,
10686 * This should be called whenever waypoints are changed
10688 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10690 struct LatLon topleft = { 0.0, 0.0 };
10691 struct LatLon bottomright = { 0.0, 0.0 };
10694 GHashTableIter iter;
10695 gpointer key, value;
10697 g_hash_table_iter_init ( &iter, vtl->waypoints );
10699 // Set bounds to first point
10700 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10701 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10702 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10705 // Ensure there is another point...
10706 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10708 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10710 // See if this point increases the bounds.
10711 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10713 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10714 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10715 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10716 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10720 vtl->waypoints_bbox.north = topleft.lat;
10721 vtl->waypoints_bbox.east = bottomright.lon;
10722 vtl->waypoints_bbox.south = bottomright.lat;
10723 vtl->waypoints_bbox.west = topleft.lon;
10726 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10728 vik_track_calculate_bounds ( trk );
10731 void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10733 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10734 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10737 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10739 if ( ! VIK_LAYER(vtl)->vt )
10742 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10743 if ( g_hash_table_size (vtl->tracks) > 1 )
10744 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10746 if ( g_hash_table_size (vtl->routes) > 1 )
10747 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10749 if ( g_hash_table_size (vtl->waypoints) > 1 )
10750 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10754 * Get the earliest timestamp available from all tracks
10756 static time_t trw_layer_get_timestamp_tracks ( VikTrwLayer *vtl )
10758 time_t timestamp = 0;
10759 GList *gl = g_hash_table_get_values ( vtl->tracks );
10760 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10761 gl = g_list_first ( gl );
10764 // Only need to check the first track as they have been sorted by time
10765 VikTrack *trk = (VikTrack*)gl->data;
10766 // Assume trackpoints already sorted by time
10767 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10768 if ( tpt && tpt->has_timestamp ) {
10769 timestamp = tpt->timestamp;
10771 g_list_free ( gl );
10777 * Get the earliest timestamp available from all waypoints
10779 static time_t trw_layer_get_timestamp_waypoints ( VikTrwLayer *vtl )
10781 time_t timestamp = 0;
10782 GList *gl = g_hash_table_get_values ( vtl->waypoints );
10784 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10785 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10786 if ( wpt->has_timestamp ) {
10787 // When timestamp not set yet - use the first value encountered
10788 if ( timestamp == 0 )
10789 timestamp = wpt->timestamp;
10790 else if ( timestamp > wpt->timestamp )
10791 timestamp = wpt->timestamp;
10794 g_list_free ( gl );
10800 * Get the earliest timestamp available for this layer
10802 static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl )
10804 time_t timestamp_tracks = trw_layer_get_timestamp_tracks ( vtl );
10805 time_t timestamp_waypoints = trw_layer_get_timestamp_waypoints ( vtl );
10806 // NB routes don't have timestamps - hence they are not considered
10808 if ( !timestamp_tracks && !timestamp_waypoints ) {
10809 // Fallback to get time from the metadata when no other timestamps available
10811 if ( vtl->metadata && vtl->metadata->timestamp && g_time_val_from_iso8601 ( vtl->metadata->timestamp, >v ) )
10814 if ( timestamp_tracks && !timestamp_waypoints )
10815 return timestamp_tracks;
10816 if ( timestamp_tracks && timestamp_waypoints && (timestamp_tracks < timestamp_waypoints) )
10817 return timestamp_tracks;
10818 return timestamp_waypoints;
10821 static void trw_layer_post_read ( VikTrwLayer *vtl, VikViewport *vvp, gboolean from_file )
10823 if ( VIK_LAYER(vtl)->realized )
10824 trw_layer_verify_thumbnails ( vtl );
10825 trw_layer_track_alloc_colors ( vtl );
10827 trw_layer_calculate_bounds_waypoints ( vtl );
10828 trw_layer_calculate_bounds_tracks ( vtl );
10830 // Apply treeview sort after loading all the tracks for this layer
10831 // (rather than sorted insert on each individual track additional)
10832 // and after subsequent changes to the properties as the specified order may have changed.
10833 // since the sorting of a treeview section is now very quick
10834 // NB sorting is also performed after every name change as well to maintain the list order
10835 trw_layer_sort_all ( vtl );
10837 // Setting metadata time if not otherwise set
10838 if ( vtl->metadata ) {
10840 gboolean need_to_set_time = TRUE;
10841 if ( vtl->metadata->timestamp ) {
10842 need_to_set_time = FALSE;
10843 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10844 need_to_set_time = TRUE;
10847 if ( need_to_set_time ) {
10848 GTimeVal timestamp;
10849 timestamp.tv_usec = 0;
10850 timestamp.tv_sec = trw_layer_get_timestamp ( vtl );
10852 // No time found - so use 'now' for the metadata time
10853 if ( timestamp.tv_sec == 0 ) {
10854 g_get_current_time ( ×tamp );
10857 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10862 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10864 return vtl->coord_mode;
10868 * Uniquify the whole layer
10869 * Also requires the layers panel as the names shown there need updating too
10870 * Returns whether the operation was successful or not
10872 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10874 if ( vtl && vlp ) {
10875 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10876 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10877 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10883 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10885 vik_coord_convert ( &(wp->coord), *dest_mode );
10888 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10890 vik_track_convert ( tr, *dest_mode );
10893 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10895 if ( vtl->coord_mode != dest_mode )
10897 vtl->coord_mode = dest_mode;
10898 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10899 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10900 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10904 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10906 vtl->menu_selection = selection;
10909 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10911 return (vtl->menu_selection);
10914 /* ----------- Downloading maps along tracks --------------- */
10916 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10918 /* TODO: calculating based on current size of viewport */
10919 const gdouble w_at_zoom_0_125 = 0.0013;
10920 const gdouble h_at_zoom_0_125 = 0.0011;
10921 gdouble zoom_factor = zoom_level/0.125;
10923 wh->lat = h_at_zoom_0_125 * zoom_factor;
10924 wh->lon = w_at_zoom_0_125 * zoom_factor;
10926 return 0; /* all OK */
10929 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10931 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10932 (dist->lat >= ABS(to->north_south - from->north_south)))
10935 VikCoord *coord = g_malloc(sizeof(VikCoord));
10936 coord->mode = VIK_COORD_LATLON;
10938 if (ABS(gradient) < 1) {
10939 if (from->east_west > to->east_west)
10940 coord->east_west = from->east_west - dist->lon;
10942 coord->east_west = from->east_west + dist->lon;
10943 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10945 if (from->north_south > to->north_south)
10946 coord->north_south = from->north_south - dist->lat;
10948 coord->north_south = from->north_south + dist->lat;
10949 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10955 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10957 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10958 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10960 VikCoord *next = from;
10962 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10964 list = g_list_prepend(list, next);
10970 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10972 typedef struct _Rect {
10977 #define GLRECT(iter) ((Rect *)((iter)->data))
10980 GList *rects_to_download = NULL;
10983 if (get_download_area_width(vvp, zoom_level, &wh))
10986 GList *iter = tr->trackpoints;
10990 gboolean new_map = TRUE;
10991 VikCoord *cur_coord, tl, br;
10994 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10996 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10997 rect = g_malloc(sizeof(Rect));
11000 rect->center = *cur_coord;
11001 rects_to_download = g_list_prepend(rects_to_download, rect);
11006 gboolean found = FALSE;
11007 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
11008 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
11019 GList *fillins = NULL;
11020 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
11021 /* seems that ATM the function get_next_coord works only for LATLON */
11022 if ( cur_coord->mode == VIK_COORD_LATLON ) {
11023 /* fill-ins for far apart points */
11024 GList *cur_rect, *next_rect;
11025 for (cur_rect = rects_to_download;
11026 (next_rect = cur_rect->next) != NULL;
11027 cur_rect = cur_rect->next) {
11028 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
11029 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
11030 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
11034 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
11037 GList *fiter = fillins;
11039 cur_coord = (VikCoord *)(fiter->data);
11040 vik_coord_set_area(cur_coord, &wh, &tl, &br);
11041 rect = g_malloc(sizeof(Rect));
11044 rect->center = *cur_coord;
11045 rects_to_download = g_list_prepend(rects_to_download, rect);
11046 fiter = fiter->next;
11050 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
11051 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
11055 for (iter = fillins; iter; iter = iter->next)
11056 g_free(iter->data);
11057 g_list_free(fillins);
11059 if (rects_to_download) {
11060 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
11061 g_free(rect_iter->data);
11062 g_list_free(rects_to_download);
11066 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
11070 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
11071 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
11072 gint selected_zoom, default_zoom;
11074 VikTrwLayer *vtl = values[MA_VTL];
11075 VikLayersPanel *vlp = values[MA_VLP];
11077 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
11078 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
11080 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
11084 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
11086 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
11087 int num_maps = g_list_length(vmls);
11090 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
11094 // Convert from list of vmls to list of names. Allowing the user to select one of them
11095 gchar **map_names = g_malloc_n(1 + num_maps, sizeof(gpointer));
11096 VikMapsLayer **map_layers = g_malloc_n(1 + num_maps, sizeof(gpointer));
11098 gchar **np = map_names;
11099 VikMapsLayer **lp = map_layers;
11101 for (i = 0; i < num_maps; i++) {
11102 vml = (VikMapsLayer *)(vmls->data);
11104 *np++ = vik_maps_layer_get_map_label(vml);
11107 // Mark end of the array lists
11111 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
11112 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
11113 if (cur_zoom == zoom_vals[default_zoom])
11116 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
11118 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
11121 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
11124 for (i = 0; i < num_maps; i++)
11125 g_free(map_names[i]);
11127 g_free(map_layers);
11133 /**** lowest waypoint number calculation ***/
11134 static gint highest_wp_number_name_to_number(const gchar *name) {
11135 if ( strlen(name) == 3 ) {
11136 int n = atoi(name);
11137 if ( n < 100 && name[0] != '0' )
11139 if ( n < 10 && name[0] != '0' )
11147 static void highest_wp_number_reset(VikTrwLayer *vtl)
11149 vtl->highest_wp_number = 0;
11152 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
11154 /* if is bigger that top, add it */
11155 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
11156 if ( new_wp_num > vtl->highest_wp_number )
11157 vtl->highest_wp_number = new_wp_num;
11160 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
11162 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
11163 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
11164 if ( vtl->highest_wp_number == old_wp_num ) {
11166 vtl->highest_wp_number--;
11168 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
11169 /* search down until we find something that *does* exist */
11171 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
11172 vtl->highest_wp_number--;
11173 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
11178 /* get lowest unused number */
11179 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
11182 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
11184 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
11185 return g_strdup(buf);
11189 * trw_layer_create_track_list_both:
11191 * Create the latest list of tracks and routes
11193 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
11195 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
11196 GList *tracks = NULL;
11197 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
11198 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
11200 return vik_trw_layer_build_track_list_t ( vtl, tracks );
11203 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
11205 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11207 gchar *title = NULL;
11208 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
11209 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
11211 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
11213 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
11217 static void trw_layer_track_list_dialog ( menu_array_layer values )
11219 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11221 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
11222 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
11226 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
11228 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11230 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
11231 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );