2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
9 * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
27 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
34 #include "vikmapslayer.h"
35 #include "vikgpslayer.h"
36 #include "viktrwlayer_tpwin.h"
37 #include "viktrwlayer_propwin.h"
38 #include "viktrwlayer_analysis.h"
39 #include "viktrwlayer_tracklist.h"
40 #include "viktrwlayer_waypointlist.h"
41 #ifdef VIK_CONFIG_GEOTAG
42 #include "viktrwlayer_geotag.h"
43 #include "geotag_exif.h"
45 #include "garminsymbols.h"
46 #include "thumbnails.h"
47 #include "background.h"
52 #include "geonamessearch.h"
53 #ifdef VIK_CONFIG_OPENSTREETMAP
54 #include "osm-traces.h"
57 #include "datasources.h"
58 #include "datasource_gps.h"
59 #include "vikexttool_datasources.h"
63 #include "vikrouting.h"
65 #include "icons/icons.h"
79 #include <gdk/gdkkeysyms.h>
81 #include <glib/gstdio.h>
82 #include <glib/gi18n.h>
84 #define VIK_TRW_LAYER_TRACK_GC 6
85 #define VIK_TRW_LAYER_TRACK_GCS 10
86 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
87 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
88 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
89 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
90 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
91 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
93 #define DRAWMODE_BY_TRACK 0
94 #define DRAWMODE_BY_SPEED 1
95 #define DRAWMODE_ALL_SAME_COLOR 2
96 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
97 // as we are (re)calculating the colour for every point
102 /* this is how it knows when you click if you are clicking close to a trackpoint. */
103 #define TRACKPOINT_SIZE_APPROX 5
104 #define WAYPOINT_SIZE_APPROX 5
106 #define MIN_STOP_LENGTH 15
107 #define MAX_STOP_LENGTH 86400
108 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
109 /* this is multiplied by user-inputted value from 1-100. */
111 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
113 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
115 FS_XX_SMALL = 0, // 'xx-small'
118 FS_MEDIUM, // DEFAULT
125 struct _VikTrwLayer {
128 GHashTable *tracks_iters;
130 GHashTable *routes_iters;
131 GHashTable *waypoints_iters;
132 GHashTable *waypoints;
133 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
134 gboolean tracks_visible, routes_visible, waypoints_visible;
135 LatLonBBox waypoints_bbox;
137 gboolean track_draw_labels;
140 guint8 drawpoints_size;
141 guint8 drawelevation;
142 guint8 elevation_factor;
146 guint8 drawdirections;
147 guint8 drawdirections_size;
148 guint8 line_thickness;
149 guint8 bg_line_thickness;
150 vik_layer_sort_order_t track_sort_order;
152 PangoLayout *tracklabellayout;
153 font_size_t track_font_size;
154 gchar *track_fsize_str;
158 gboolean wp_draw_symbols;
159 font_size_t wp_font_size;
161 vik_layer_sort_order_t wp_sort_order;
163 gdouble track_draw_speed_factor;
165 GdkGC *track_1color_gc;
166 GdkColor track_color;
167 GdkGC *current_track_gc;
168 // Separate GC for a track's potential new point as drawn via separate method
169 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
170 GdkGC *current_track_newpoint_gc;
171 GdkGC *track_bg_gc; GdkColor track_bg_color;
172 GdkGC *waypoint_gc; GdkColor waypoint_color;
173 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
174 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
176 GdkFont *waypoint_font;
177 VikTrack *current_track; // ATM shared between new tracks and new routes
178 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
179 gboolean draw_sync_done;
180 gboolean draw_sync_do;
182 VikCoordMode coord_mode;
184 /* wp editing tool */
185 VikWaypoint *current_wp;
186 gpointer current_wp_id;
188 gboolean waypoint_rightclick;
190 /* track editing tool */
192 VikTrack *current_tp_track;
193 gpointer current_tp_id;
194 VikTrwLayerTpwin *tpwin;
196 /* track editing tool -- more specifically, moving tps */
199 /* route finder tool */
200 gboolean route_finder_started;
201 VikCoord route_finder_coord;
202 gboolean route_finder_check_added_track;
203 VikTrack *route_finder_added_track;
204 VikTrack *route_finder_current_track;
205 gboolean route_finder_append;
212 guint16 image_cache_size;
214 /* for waypoint text */
215 PangoLayout *wplabellayout;
217 gboolean has_verified_thumbnails;
219 GtkMenu *wp_right_click_menu;
220 GtkMenu *track_right_click_menu;
223 VikStdLayerMenuItem menu_selection;
225 gint highest_wp_number;
228 GtkWidget *tracks_analysis_dialog;
231 /* A caached waypoint image. */
234 gchar *image; /* filename */
237 struct DrawingParams {
242 guint16 width, height;
243 gdouble cc; // Cosine factor in track directions
244 gdouble ss; // Sine factor in track directions
245 const VikCoord *center;
246 gboolean one_zone, lat_lon;
247 gdouble ce1, ce2, cn1, cn2;
251 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
256 MA_SUBTYPE, // OR END for Layer only
265 typedef gpointer menu_array_layer[2];
266 typedef gpointer menu_array_sublayer[MA_LAST];
268 static void trw_layer_delete_item ( menu_array_sublayer values );
269 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
270 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
272 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
273 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
275 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
276 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
278 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
279 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
281 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
282 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
283 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
284 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
285 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
286 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
287 static void trw_layer_goto_track_center ( menu_array_sublayer values );
288 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
289 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
290 static void trw_layer_merge_with_other ( menu_array_sublayer values );
291 static void trw_layer_append_track ( menu_array_sublayer values );
292 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
293 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
294 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
295 static void trw_layer_split_segments ( menu_array_sublayer values );
296 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
297 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
298 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
299 static void trw_layer_reverse ( menu_array_sublayer values );
300 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
301 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
302 static void trw_layer_show_picture ( menu_array_sublayer values );
303 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
305 static void trw_layer_centerize ( menu_array_layer values );
306 static void trw_layer_auto_view ( menu_array_layer values );
307 static void trw_layer_export ( menu_array_layer values, const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
308 static void trw_layer_goto_wp ( menu_array_layer values );
309 static void trw_layer_new_wp ( menu_array_layer values );
310 static void trw_layer_new_track ( menu_array_layer values );
311 static void trw_layer_new_route ( menu_array_layer values );
312 static void trw_layer_finish_track ( menu_array_layer values );
313 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
314 static void trw_layer_auto_tracks_view ( menu_array_layer values );
315 static void trw_layer_delete_all_tracks ( menu_array_layer values );
316 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
317 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
318 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
319 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
320 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
321 #ifdef VIK_CONFIG_GEOTAG
322 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
323 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
324 static void trw_layer_geotagging_track ( menu_array_sublayer values );
325 static void trw_layer_geotagging ( menu_array_layer values );
327 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
328 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
329 static void trw_layer_acquire_url_cb ( menu_array_layer values );
330 #ifdef VIK_CONFIG_OPENSTREETMAP
331 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
332 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
334 #ifdef VIK_CONFIG_GEOCACHES
335 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
337 #ifdef VIK_CONFIG_GEOTAG
338 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
340 static void trw_layer_acquire_file_cb ( menu_array_layer values );
341 static void trw_layer_gps_upload ( menu_array_layer values );
343 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
344 static void trw_layer_track_list_dialog ( menu_array_layer values );
345 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
347 // Specific route versions:
348 // Most track handling functions can handle operating on the route list
349 // However these ones are easier in separate functions
350 static void trw_layer_auto_routes_view ( menu_array_layer values );
351 static void trw_layer_delete_all_routes ( menu_array_layer values );
352 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
355 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
356 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
357 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
358 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
360 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
361 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
362 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
364 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
365 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
366 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
367 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
369 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
370 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
371 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
372 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
373 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
374 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
375 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
376 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
377 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
378 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
379 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
380 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
381 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
382 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
383 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
384 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
385 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
386 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
387 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
388 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
389 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
391 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393 static void cached_pixbuf_free ( CachedPixbuf *cp );
394 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
396 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
397 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
399 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
400 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
402 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
403 static void highest_wp_number_reset(VikTrwLayer *vtl);
404 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
405 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
407 // Note for the following tool GtkRadioActionEntry texts:
408 // the very first text value is an internal name not displayed anywhere
409 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
410 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
411 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
412 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
413 static VikToolInterface trw_layer_tools[] = {
414 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
415 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
416 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
418 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
420 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
421 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
422 (VikToolMouseFunc) tool_new_track_click,
423 (VikToolMouseMoveFunc) tool_new_track_move,
424 (VikToolMouseFunc) tool_new_track_release,
425 (VikToolKeyFunc) tool_new_track_key_press,
426 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
427 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
429 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
430 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
431 (VikToolMouseFunc) tool_new_route_click,
432 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
433 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
434 (VikToolKeyFunc) tool_new_track_key_press, // -/#
435 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
436 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
438 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
439 (VikToolConstructorFunc) tool_edit_waypoint_create,
440 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
442 (VikToolMouseFunc) tool_edit_waypoint_click,
443 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
444 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
446 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
448 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
449 (VikToolConstructorFunc) tool_edit_trackpoint_create,
450 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
452 (VikToolMouseFunc) tool_edit_trackpoint_click,
453 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
454 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
456 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
458 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
459 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
460 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
462 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
464 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
465 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
466 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
468 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
472 TOOL_CREATE_WAYPOINT=0,
476 TOOL_EDIT_TRACKPOINT,
482 /****** PARAMETERS ******/
484 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced") };
485 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV };
487 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
488 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
490 #define MIN_POINT_SIZE 2
491 #define MAX_POINT_SIZE 10
493 #define MIN_ARROW_SIZE 3
494 #define MAX_ARROW_SIZE 20
496 static VikLayerParamScale params_scales[] = {
497 /* min max step digits */
498 { 1, 10, 1, 0 }, /* line_thickness */
499 { 0, 100, 1, 0 }, /* track draw speed factor */
500 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
501 /* 5 * step == how much to turn */
502 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
503 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
504 { 5, 500, 5, 0 }, // 5: image cache_size - " "
505 { 0, 8, 1, 0 }, // 6: Background line thickness
506 { 1, 64, 1, 0 }, /* wpsize */
507 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
508 { 1, 100, 1, 0 }, // 9: elevation factor
509 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
510 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
513 static gchar* params_font_sizes[] = {
514 N_("Extra Extra Small"),
520 N_("Extra Extra Large"),
523 // Needs to align with vik_layer_sort_order_t
524 static gchar* params_sort_order[] = {
526 N_("Name Ascending"),
527 N_("Name Descending"),
531 static VikLayerParamData black_color_default ( void ) {
532 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
534 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
535 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
536 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
537 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
538 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
539 static VikLayerParamData trackbgcolor_default ( void ) {
540 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
542 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
543 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
544 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
546 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
547 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
548 static VikLayerParamData wptextcolor_default ( void ) {
549 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
551 static VikLayerParamData wpbgcolor_default ( void ) {
552 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
554 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
555 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
557 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
558 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
559 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
561 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
563 VikLayerParam trw_layer_params[] = {
564 { 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 },
565 { 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 },
566 { 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 },
568 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
569 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
570 { 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 },
571 { 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 },
572 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
573 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
574 { 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 },
575 { 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 },
576 { 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 },
577 { 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 },
578 { 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 },
579 { 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 },
580 { 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 },
581 { 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 },
582 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
583 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 },
584 { 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 },
586 { 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 },
587 { 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 },
588 { 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,
589 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
590 { 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 },
592 { 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 },
593 { 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 },
594 { 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 },
595 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
596 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
597 { 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 },
598 { 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 },
599 { 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 },
600 { 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 },
601 { 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 },
603 { 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 },
604 { 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 },
605 { 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 },
606 { 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 },
609 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
611 // Sublayer visibilities
654 *** 1) Add to trw_layer_params and enumeration
655 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
658 /****** END PARAMETERS ******/
660 /* Layer Interface function definitions */
661 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
662 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
663 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
664 static void trw_layer_free ( VikTrwLayer *trwlayer );
665 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
666 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
667 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
668 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
669 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
670 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
671 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
672 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
673 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
674 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
675 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
676 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
677 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
678 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
679 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
680 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
681 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
682 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
683 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
684 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
685 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
686 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
687 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
688 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
689 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
690 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
691 /* End Layer Interface function definitions */
693 VikLayerInterface vik_trw_layer_interface = {
700 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
704 params_groups, /* params_groups */
705 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
709 (VikLayerFuncCreate) trw_layer_create,
710 (VikLayerFuncRealize) trw_layer_realize,
711 (VikLayerFuncPostRead) trw_layer_post_read,
712 (VikLayerFuncFree) trw_layer_free,
714 (VikLayerFuncProperties) NULL,
715 (VikLayerFuncDraw) trw_layer_draw,
716 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
718 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
719 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
721 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
722 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
724 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
725 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
726 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
727 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
728 (VikLayerFuncLayerSelected) trw_layer_selected,
730 (VikLayerFuncMarshall) trw_layer_marshall,
731 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
733 (VikLayerFuncSetParam) trw_layer_set_param,
734 (VikLayerFuncGetParam) trw_layer_get_param,
735 (VikLayerFuncChangeParam) trw_layer_change_param,
737 (VikLayerFuncReadFileData) a_gpspoint_read_file,
738 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
740 (VikLayerFuncDeleteItem) trw_layer_del_item,
741 (VikLayerFuncCutItem) trw_layer_cut_item,
742 (VikLayerFuncCopyItem) trw_layer_copy_item,
743 (VikLayerFuncPasteItem) trw_layer_paste_item,
744 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
746 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
748 (VikLayerFuncSelectClick) trw_layer_select_click,
749 (VikLayerFuncSelectMove) trw_layer_select_move,
750 (VikLayerFuncSelectRelease) trw_layer_select_release,
751 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
754 GType vik_trw_layer_get_type ()
756 static GType vtl_type = 0;
760 static const GTypeInfo vtl_info =
762 sizeof (VikTrwLayerClass),
763 NULL, /* base_init */
764 NULL, /* base_finalize */
765 NULL, /* class init */
766 NULL, /* class_finalize */
767 NULL, /* class_data */
768 sizeof (VikTrwLayer),
770 NULL /* instance init */
772 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
778 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
780 static menu_array_sublayer values;
786 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
789 values[MA_VTL] = vtl;
790 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
791 values[MA_SUBLAYER_ID] = sublayer;
792 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
794 trw_layer_delete_item ( values );
797 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
799 static menu_array_sublayer values;
805 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
808 values[MA_VTL] = vtl;
809 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
810 values[MA_SUBLAYER_ID] = sublayer;
811 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
813 trw_layer_copy_item_cb(values);
814 trw_layer_cut_item_cb(values);
817 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
819 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
820 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
821 gpointer * sublayer = values[MA_SUBLAYER_ID];
825 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
829 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
830 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
831 if ( wp && wp->name )
834 name = NULL; // Broken :(
836 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
837 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
838 if ( trk && trk->name )
841 name = NULL; // Broken :(
844 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
845 if ( trk && trk->name )
848 name = NULL; // Broken :(
851 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
852 subtype, len, name, data);
856 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
858 trw_layer_copy_item_cb(values);
859 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
860 trw_layer_delete_item(values);
863 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
865 // Slightly cheating method, routing via the panels capability
866 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
869 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
879 GByteArray *ba = g_byte_array_new ();
881 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
882 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
883 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
884 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
886 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
889 g_byte_array_append ( ba, id, il );
897 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
904 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
908 w = vik_waypoint_unmarshall ( item, len );
909 // When copying - we'll create a new name based on the original
910 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
911 vik_trw_layer_add_waypoint ( vtl, name, w );
912 waypoint_convert (NULL, w, &vtl->coord_mode);
915 trw_layer_calculate_bounds_waypoints ( vtl );
917 // Consider if redraw necessary for the new item
918 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
919 vik_layer_emit_update ( VIK_LAYER(vtl) );
922 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
926 t = vik_track_unmarshall ( item, len );
927 // When copying - we'll create a new name based on the original
928 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
929 vik_trw_layer_add_track ( vtl, name, t );
930 vik_track_convert (t, vtl->coord_mode);
933 // Consider if redraw necessary for the new item
934 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
935 vik_layer_emit_update ( VIK_LAYER(vtl) );
938 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
942 t = vik_track_unmarshall ( item, len );
943 // When copying - we'll create a new name based on the original
944 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
945 vik_trw_layer_add_route ( vtl, name, t );
946 vik_track_convert (t, vtl->coord_mode);
949 // Consider if redraw necessary for the new item
950 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
951 vik_layer_emit_update ( VIK_LAYER(vtl) );
957 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
964 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
968 case PARAM_TV: vtl->tracks_visible = data.b; break;
969 case PARAM_WV: vtl->waypoints_visible = data.b; break;
970 case PARAM_RV: vtl->routes_visible = data.b; break;
971 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
972 case PARAM_TLFONTSIZE:
973 if ( data.u < FS_NUM_SIZES ) {
974 vtl->track_font_size = data.u;
975 g_free ( vtl->track_fsize_str );
976 switch ( vtl->track_font_size ) {
977 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
978 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
979 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
980 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
981 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
982 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
983 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
987 case PARAM_DM: vtl->drawmode = data.u; break;
989 vtl->track_color = data.c;
990 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
992 case PARAM_DP: vtl->drawpoints = data.b; break;
994 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
995 vtl->drawpoints_size = data.u;
997 case PARAM_DE: vtl->drawelevation = data.b; break;
998 case PARAM_DS: vtl->drawstops = data.b; break;
999 case PARAM_DL: vtl->drawlines = data.b; break;
1000 case PARAM_DD: vtl->drawdirections = data.b; break;
1002 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1003 vtl->drawdirections_size = data.u;
1005 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1006 vtl->stop_length = data.u;
1008 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1009 vtl->elevation_factor = data.u;
1011 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1013 vtl->line_thickness = data.u;
1014 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1017 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1019 vtl->bg_line_thickness = data.u;
1020 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1024 vtl->track_bg_color = data.c;
1025 if ( vtl->track_bg_gc )
1026 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1028 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1029 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1030 case PARAM_DLA: vtl->drawlabels = data.b; break;
1031 case PARAM_DI: vtl->drawimages = data.b; break;
1032 case PARAM_IS: if ( data.u != vtl->image_size )
1034 vtl->image_size = data.u;
1035 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1036 g_queue_free ( vtl->image_cache );
1037 vtl->image_cache = g_queue_new ();
1040 case PARAM_IA: vtl->image_alpha = data.u; break;
1041 case PARAM_ICS: vtl->image_cache_size = data.u;
1042 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1043 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1046 vtl->waypoint_color = data.c;
1047 if ( vtl->waypoint_gc )
1048 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1051 vtl->waypoint_text_color = data.c;
1052 if ( vtl->waypoint_text_gc )
1053 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1056 vtl->waypoint_bg_color = data.c;
1057 if ( vtl->waypoint_bg_gc )
1058 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1061 vtl->wpbgand = data.b;
1062 if ( vtl->waypoint_bg_gc )
1063 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1065 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1066 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1067 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1068 case PARAM_WPFONTSIZE:
1069 if ( data.u < FS_NUM_SIZES ) {
1070 vtl->wp_font_size = data.u;
1071 g_free ( vtl->wp_fsize_str );
1072 switch ( vtl->wp_font_size ) {
1073 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1074 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1075 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1076 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1077 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1078 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1079 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1083 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1088 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1090 VikLayerParamData rv;
1093 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1094 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1095 case PARAM_RV: rv.b = vtl->routes_visible; break;
1096 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1097 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1098 case PARAM_DM: rv.u = vtl->drawmode; break;
1099 case PARAM_TC: rv.c = vtl->track_color; break;
1100 case PARAM_DP: rv.b = vtl->drawpoints; break;
1101 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1102 case PARAM_DE: rv.b = vtl->drawelevation; break;
1103 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1104 case PARAM_DS: rv.b = vtl->drawstops; break;
1105 case PARAM_SL: rv.u = vtl->stop_length; break;
1106 case PARAM_DL: rv.b = vtl->drawlines; break;
1107 case PARAM_DD: rv.b = vtl->drawdirections; break;
1108 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1109 case PARAM_LT: rv.u = vtl->line_thickness; break;
1110 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1111 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1112 case PARAM_DI: rv.b = vtl->drawimages; break;
1113 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1114 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1115 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1116 case PARAM_IS: rv.u = vtl->image_size; break;
1117 case PARAM_IA: rv.u = vtl->image_alpha; break;
1118 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1119 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1120 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1121 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1122 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1123 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1124 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1125 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1126 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1127 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1132 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1134 // This '-3' is to account for the first few parameters not in the properties
1135 const gint OFFSET = -3;
1137 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1138 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1141 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1142 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1143 GtkWidget **ww2 = values[UI_CHG_LABELS];
1144 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1145 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1146 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1147 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1148 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1149 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1150 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1151 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1152 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1153 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1154 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1155 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1158 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1161 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1162 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1163 GtkWidget **ww2 = values[UI_CHG_LABELS];
1164 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1165 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1166 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1167 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1168 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1169 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1170 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1171 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1172 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1173 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1174 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1175 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1176 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1177 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1178 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1179 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1182 // Alter sensitivity of all track colours according to the draw track mode.
1185 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1186 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1187 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1188 GtkWidget **ww2 = values[UI_CHG_LABELS];
1189 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1190 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1191 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1192 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1195 // NB Since other track settings have been split across tabs,
1196 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1201 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1208 // Use byte arrays to store sublayer data
1209 // much like done elsewhere e.g. vik_layer_marshall_params()
1210 GByteArray *ba = g_byte_array_new ( );
1215 guint object_length;
1218 // the length of the item
1219 // the sublayer type of item
1220 // the the actual item
1221 #define tlm_append(object_pointer, size, type) \
1223 object_length = (size); \
1224 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1225 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1226 g_byte_array_append ( ba, (object_pointer), object_length );
1228 // Layer parameters first
1229 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1230 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1231 g_byte_array_append ( ba, pd, pl );
1234 // Now sublayer data
1235 GHashTableIter iter;
1236 gpointer key, value;
1239 g_hash_table_iter_init ( &iter, vtl->waypoints );
1240 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1241 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1242 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1247 g_hash_table_iter_init ( &iter, vtl->tracks );
1248 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1249 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1250 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1255 g_hash_table_iter_init ( &iter, vtl->routes );
1256 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1257 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1258 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1268 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1270 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1272 gint consumed_length;
1274 // First the overall layer parameters
1275 memcpy(&pl, data, sizeof(pl));
1277 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1280 consumed_length = pl;
1281 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1283 #define tlm_size (*(gint *)data)
1284 // See marshalling above for order of how this is written
1286 data += sizeof_len_and_subtype + tlm_size;
1288 // Now the individual sublayers:
1290 while ( *data && consumed_length < len ) {
1291 // Normally four extra bytes at the end of the datastream
1292 // (since it's a GByteArray and that's where it's length is stored)
1293 // So only attempt read when there's an actual block of sublayer data
1294 if ( consumed_length + tlm_size < len ) {
1296 // Reuse pl to read the subtype from the data stream
1297 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1299 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1300 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1301 gchar *name = g_strdup ( trk->name );
1302 vik_trw_layer_add_track ( vtl, name, trk );
1305 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1306 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1307 gchar *name = g_strdup ( wp->name );
1308 vik_trw_layer_add_waypoint ( vtl, name, wp );
1311 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1312 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1313 gchar *name = g_strdup ( trk->name );
1314 vik_trw_layer_add_route ( vtl, name, trk );
1318 consumed_length += tlm_size + sizeof_len_and_subtype;
1321 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1323 // Not stored anywhere else so need to regenerate
1324 trw_layer_calculate_bounds_waypoints ( vtl );
1329 // Keep interesting hash function at least visible
1331 static guint strcase_hash(gconstpointer v)
1333 // 31 bit hash function
1336 gchar s[128]; // malloc is too slow for reading big files
1339 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1340 p[i] = toupper(t[i]);
1346 for (p += 1; *p != '\0'; p++)
1347 h = (h << 5) - h + *p;
1354 // Stick a 1 at the end of the function name to make it more unique
1355 // thus more easily searchable in a simple text editor
1356 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1358 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1359 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1361 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1362 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1364 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1365 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1366 // and with normal PC processing capabilities - it has negligibile performance impact
1367 // This also minimized the amount of rework - as the management of the hash tables already exists.
1369 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1370 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1371 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1373 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1374 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1375 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1376 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1377 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1378 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1380 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1382 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1384 // Param settings that are not available via the GUI
1385 // Force to on after processing params (which defaults them to off with a zero value)
1386 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1388 rv->draw_sync_done = TRUE;
1389 rv->draw_sync_do = TRUE;
1390 // Everything else is 0, FALSE or NULL
1396 static void trw_layer_free ( VikTrwLayer *trwlayer )
1398 g_hash_table_destroy(trwlayer->waypoints);
1399 g_hash_table_destroy(trwlayer->waypoints_iters);
1400 g_hash_table_destroy(trwlayer->tracks);
1401 g_hash_table_destroy(trwlayer->tracks_iters);
1402 g_hash_table_destroy(trwlayer->routes);
1403 g_hash_table_destroy(trwlayer->routes_iters);
1405 /* ODC: replace with GArray */
1406 trw_layer_free_track_gcs ( trwlayer );
1408 if ( trwlayer->wp_right_click_menu )
1409 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1411 if ( trwlayer->track_right_click_menu )
1412 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1414 if ( trwlayer->tracklabellayout != NULL)
1415 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1417 if ( trwlayer->wplabellayout != NULL)
1418 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1420 if ( trwlayer->waypoint_gc != NULL )
1421 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1423 if ( trwlayer->waypoint_text_gc != NULL )
1424 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1426 if ( trwlayer->waypoint_bg_gc != NULL )
1427 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1429 g_free ( trwlayer->wp_fsize_str );
1430 g_free ( trwlayer->track_fsize_str );
1432 if ( trwlayer->tpwin != NULL )
1433 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1435 if ( trwlayer->tracks_analysis_dialog != NULL )
1436 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1438 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1439 g_queue_free ( trwlayer->image_cache );
1442 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1446 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1447 dp->xmpp = vik_viewport_get_xmpp ( vp );
1448 dp->ympp = vik_viewport_get_ympp ( vp );
1449 dp->width = vik_viewport_get_width ( vp );
1450 dp->height = vik_viewport_get_height ( vp );
1451 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1452 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1454 dp->center = vik_viewport_get_center ( vp );
1455 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1456 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1461 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1462 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1463 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1465 dp->ce1 = dp->center->east_west-w2;
1466 dp->ce2 = dp->center->east_west+w2;
1467 dp->cn1 = dp->center->north_south-h2;
1468 dp->cn2 = dp->center->north_south+h2;
1469 } else if ( dp->lat_lon ) {
1470 VikCoord upperleft, bottomright;
1471 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1472 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1473 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1474 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1475 dp->ce1 = upperleft.east_west;
1476 dp->ce2 = bottomright.east_west;
1477 dp->cn1 = bottomright.north_south;
1478 dp->cn2 = upperleft.north_south;
1481 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1485 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1486 * Here a simple traffic like light colour system is used:
1487 * . slow points are red
1488 * . average is yellow
1489 * . fast points are green
1491 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1494 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1495 if ( average_speed > 0 ) {
1496 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1497 if ( rv < low_speed )
1498 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1499 else if ( rv > high_speed )
1500 return VIK_TRW_LAYER_TRACK_GC_FAST;
1502 return VIK_TRW_LAYER_TRACK_GC_AVER;
1505 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1508 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1510 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1511 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1512 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1513 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1517 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1519 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1521 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1522 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1524 // Fallback if parse failure
1525 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1527 g_free ( label_markup );
1529 gint label_x, label_y;
1531 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1533 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1534 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1538 * distance_in_preferred_units:
1539 * @dist: The source distance in standard SI Units (i.e. metres)
1541 * TODO: This is a generic function that could be moved into globals.c or utils.c
1543 * Probably best used if you have a only few conversions to perform.
1544 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1545 * since it will be doing the preference check on each call
1547 * Returns: The distance in the units as specified by the preferences
1549 static gdouble distance_in_preferred_units ( gdouble dist )
1552 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1553 switch (dist_units) {
1554 case VIK_UNITS_DISTANCE_MILES:
1555 mydist = VIK_METERS_TO_MILES(dist);
1557 // VIK_UNITS_DISTANCE_KILOMETRES:
1559 mydist = dist/1000.0;
1566 * trw_layer_draw_dist_labels:
1568 * Draw a few labels along a track at nicely seperated distances
1569 * This might slow things down if there's many tracks being displayed with this on.
1571 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1573 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1574 25.0, 40.0, 50.0, 75.0, 100.0,
1575 150.0, 200.0, 250.0, 500.0, 1000.0};
1577 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1579 // Convert to specified unit to find the friendly breakdown value
1580 dist = distance_in_preferred_units ( dist );
1584 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1585 if ( chunksd[i] > dist ) {
1587 dist = chunksd[index];
1592 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1594 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1595 gdouble dist_i = dist * i;
1597 // Convert distance back into metres for use in finding a trackpoint
1598 switch (dist_units) {
1599 case VIK_UNITS_DISTANCE_MILES:
1600 dist_i = VIK_MILES_TO_METERS(dist_i);
1602 // VIK_UNITS_DISTANCE_KILOMETRES:
1604 dist_i = dist_i*1000.0;
1608 gdouble dist_current = 0.0;
1609 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1610 gdouble dist_next = 0.0;
1611 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1613 gdouble dist_between_tps = fabs (dist_next - dist_current);
1614 gdouble ratio = 0.0;
1615 // Prevent division by 0 errors
1616 if ( dist_between_tps > 0.0 )
1617 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1619 if ( tp_current && tp_next ) {
1620 // Construct the name based on the distance value
1623 switch (dist_units) {
1624 case VIK_UNITS_DISTANCE_MILES:
1625 units = g_strdup ( _("miles") );
1627 // VIK_UNITS_DISTANCE_KILOMETRES:
1629 units = g_strdup ( _("km") );
1633 // Convert for display
1634 dist_i = distance_in_preferred_units ( dist_i );
1636 // Make the precision of the output related to the unit size.
1638 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1639 else if ( index == 1 )
1640 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1642 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1645 struct LatLon ll_current, ll_next;
1646 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1647 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1649 // positional interpolation
1650 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1651 // but should be good enough over the small scale that I anticipate usage on
1652 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1653 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1655 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1658 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1659 fgcolour = gdk_color_to_string ( &(trk->color) );
1661 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1663 // if highlight mode on, then colour the background in the highlight colour
1665 if ( drawing_highlight )
1666 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1668 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1670 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1672 g_free ( fgcolour );
1673 g_free ( bgcolour );
1680 * trw_layer_draw_track_name_labels:
1682 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1684 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1687 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1688 fgcolour = gdk_color_to_string ( &(trk->color) );
1690 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1692 // if highlight mode on, then colour the background in the highlight colour
1694 if ( drawing_highlight )
1695 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1697 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1699 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1701 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1702 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1703 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1704 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1705 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1706 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1708 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1710 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1713 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1714 // No other labels to draw
1717 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1720 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1723 VikCoord begin_coord = tp_begin->coord;
1724 VikCoord end_coord = tp_end->coord;
1726 gboolean done_start_end = FALSE;
1728 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1729 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1731 // This number can be configured via the settings if you really want to change it
1732 gdouble distance_diff;
1733 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1734 distance_diff = 100.0; // Metres
1736 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1737 // Start and end 'close' together so only draw one label at an average location
1738 gint x1, x2, y1, y2;
1739 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1740 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1742 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1744 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1745 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1748 done_start_end = TRUE;
1752 if ( ! done_start_end ) {
1753 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1754 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1755 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1756 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1757 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1758 g_free ( name_start );
1760 // Don't draw end label if this is the one being created
1761 if ( trk != dp->vtl->current_track ) {
1762 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1763 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1764 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1765 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1766 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1767 g_free ( name_end );
1772 g_free ( fgcolour );
1773 g_free ( bgcolour );
1777 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1779 if ( ! track->visible )
1782 /* TODO: this function is a mess, get rid of any redundancy */
1783 GList *list = track->trackpoints;
1785 gboolean useoldvals = TRUE;
1787 gboolean drawpoints;
1789 gboolean drawelevation;
1790 gdouble min_alt, max_alt, alt_diff = 0;
1792 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1793 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1796 if ( dp->vtl->drawelevation )
1798 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1799 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1800 alt_diff = max_alt - min_alt;
1803 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1804 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1805 trw_layer_draw_track ( id, track, dp, TRUE );
1807 if ( draw_track_outline )
1808 drawpoints = drawstops = FALSE;
1810 drawpoints = dp->vtl->drawpoints;
1811 drawstops = dp->vtl->drawstops;
1814 gboolean drawing_highlight = FALSE;
1815 /* Current track - used for creation */
1816 if ( track == dp->vtl->current_track )
1817 main_gc = dp->vtl->current_track_gc;
1819 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1820 /* Draw all tracks of the layer in special colour */
1821 /* if track is member of selected layer or is the current selected track
1822 then draw in the highlight colour.
1823 NB this supercedes the drawmode */
1824 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1825 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1826 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1827 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1828 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1829 drawing_highlight = TRUE;
1832 if ( !drawing_highlight ) {
1833 // Still need to figure out the gc according to the drawing mode:
1834 switch ( dp->vtl->drawmode ) {
1835 case DRAWMODE_BY_TRACK:
1836 if ( dp->vtl->track_1color_gc )
1837 g_object_unref ( dp->vtl->track_1color_gc );
1838 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1839 main_gc = dp->vtl->track_1color_gc;
1842 // Mostly for DRAWMODE_ALL_SAME_COLOR
1843 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1844 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1851 int x, y, oldx, oldy;
1852 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1854 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1856 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1858 // Draw the first point as something a bit different from the normal points
1859 // ATM it's slightly bigger and a triangle
1861 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1862 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1868 gdouble average_speed = 0.0;
1869 gdouble low_speed = 0.0;
1870 gdouble high_speed = 0.0;
1871 // If necessary calculate these values - which is done only once per track redraw
1872 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1873 // the percentage factor away from the average speed determines transistions between the levels
1874 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1875 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1876 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1879 while ((list = g_list_next(list)))
1881 tp = VIK_TRACKPOINT(list->data);
1882 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1884 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1885 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1886 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1887 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1888 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1890 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1893 * If points are the same in display coordinates, don't draw.
1895 if ( useoldvals && x == oldx && y == oldy )
1897 // Still need to process points to ensure 'stops' are drawn if required
1898 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1899 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1900 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
1905 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1906 if ( drawpoints || dp->vtl->drawlines ) {
1907 // setup main_gc for both point and line drawing
1908 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1909 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 ) );
1913 if ( drawpoints && ! draw_track_outline )
1918 * The concept of drawing stops is that a trackpoint
1919 * that is if the next trackpoint has a timestamp far into
1920 * the future, we draw a circle of 6x trackpoint size,
1921 * instead of a rectangle of 2x trackpoint size.
1922 * This is drawn first so the trackpoint will be drawn on top
1925 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1926 /* Stop point. Draw 6x circle. Always in redish colour */
1927 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 );
1929 /* Regular point - draw 2x square. */
1930 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1933 /* Final point - draw 4x circle. */
1934 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 );
1937 if ((!tp->newsegment) && (dp->vtl->drawlines))
1940 /* UTM only: zone check */
1941 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1942 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1945 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1947 if ( draw_track_outline ) {
1948 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1952 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1954 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1956 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1961 tmp[1].y = oldy-FIXALTITUDE(list->data);
1963 tmp[2].y = y-FIXALTITUDE(list->next->data);
1968 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1969 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1971 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1972 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1974 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1979 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1980 // Draw an arrow at the mid point to show the direction of the track
1981 // Code is a rework from vikwindow::draw_ruler()
1982 gint midx = (oldx + x) / 2;
1983 gint midy = (oldy + y) / 2;
1985 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1986 // Avoid divide by zero and ensure at least 1 pixel big
1988 gdouble dx = (oldx - midx) / len;
1989 gdouble dy = (oldy - midy) / len;
1990 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1991 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2001 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2003 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2004 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2006 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2008 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2009 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 ));
2013 * If points are the same in display coordinates, don't draw.
2015 if ( x != oldx || y != oldy )
2017 if ( draw_track_outline )
2018 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2020 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2026 * If points are the same in display coordinates, don't draw.
2028 if ( x != oldx && y != oldy )
2030 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2031 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2039 // Labels drawn after the trackpoints, so the labels are on top
2040 if ( dp->vtl->track_draw_labels ) {
2041 if ( track->max_number_dist_labels > 0 ) {
2042 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2045 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2046 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2052 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2054 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2055 trw_layer_draw_track ( id, track, dp, FALSE );
2059 static void cached_pixbuf_free ( CachedPixbuf *cp )
2061 g_object_unref ( G_OBJECT(cp->pixbuf) );
2062 g_free ( cp->image );
2065 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2067 return strcmp ( cp->image, name );
2070 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2073 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2074 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2075 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2078 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2080 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2082 if ( wp->image && dp->vtl->drawimages )
2084 GdkPixbuf *pixbuf = NULL;
2087 if ( dp->vtl->image_alpha == 0)
2090 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2092 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2095 gchar *image = wp->image;
2096 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2097 if ( ! regularthumb )
2099 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2100 image = "\x12\x00"; /* this shouldn't occur naturally. */
2104 CachedPixbuf *cp = NULL;
2105 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2106 if ( dp->vtl->image_size == 128 )
2107 cp->pixbuf = regularthumb;
2110 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2111 g_assert ( cp->pixbuf );
2112 g_object_unref ( G_OBJECT(regularthumb) );
2114 cp->image = g_strdup ( image );
2116 /* needed so 'click picture' tool knows how big the pic is; we don't
2117 * store it in cp because they may have been freed already. */
2118 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2119 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2121 g_queue_push_head ( dp->vtl->image_cache, cp );
2122 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2123 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2125 pixbuf = cp->pixbuf;
2129 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2135 w = gdk_pixbuf_get_width ( pixbuf );
2136 h = gdk_pixbuf_get_height ( pixbuf );
2138 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2140 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2141 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2142 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2143 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2144 // Highlighted - so draw a little border around the chosen one
2145 // single line seems a little weak so draw 2 of them
2146 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2147 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2148 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2149 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2152 if ( dp->vtl->image_alpha == 255 )
2153 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2155 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2157 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2161 // Draw appropriate symbol - either symbol image or simple types
2162 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2163 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 );
2165 else if ( wp == dp->vtl->current_wp ) {
2166 switch ( dp->vtl->wp_symbol ) {
2167 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;
2168 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;
2169 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;
2170 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 );
2171 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 );
2175 switch ( dp->vtl->wp_symbol ) {
2176 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;
2177 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;
2178 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;
2179 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 );
2180 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;
2184 if ( dp->vtl->drawlabels )
2186 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2187 gint label_x, label_y;
2189 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2191 // Could this stored in the waypoint rather than recreating each pass?
2192 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2194 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2195 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2197 // Fallback if parse failure
2198 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2200 g_free ( wp_label_markup );
2202 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2203 label_x = x - width/2;
2204 if ( wp->symbol_pixbuf )
2205 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2207 label_y = y - dp->vtl->wp_size - height - 2;
2209 /* if highlight mode on, then draw background text in highlight colour */
2210 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2211 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2212 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2213 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2214 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2216 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2219 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2221 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2226 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2228 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2229 trw_layer_draw_waypoint ( id, wp, dp );
2233 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2235 static struct DrawingParams dp;
2236 g_assert ( l != NULL );
2238 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2240 if ( l->tracks_visible )
2241 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2243 if ( l->routes_visible )
2244 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2246 if (l->waypoints_visible)
2247 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2250 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2253 if ( vtl->track_bg_gc )
2255 g_object_unref ( vtl->track_bg_gc );
2256 vtl->track_bg_gc = NULL;
2258 if ( vtl->track_1color_gc )
2260 g_object_unref ( vtl->track_1color_gc );
2261 vtl->track_1color_gc = NULL;
2263 if ( vtl->current_track_gc )
2265 g_object_unref ( vtl->current_track_gc );
2266 vtl->current_track_gc = NULL;
2268 if ( vtl->current_track_newpoint_gc )
2270 g_object_unref ( vtl->current_track_newpoint_gc );
2271 vtl->current_track_newpoint_gc = NULL;
2274 if ( ! vtl->track_gc )
2276 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2277 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2278 g_array_free ( vtl->track_gc, TRUE );
2279 vtl->track_gc = NULL;
2282 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2284 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2285 gint width = vtl->line_thickness;
2287 if ( vtl->track_gc )
2288 trw_layer_free_track_gcs ( vtl );
2290 if ( vtl->track_bg_gc )
2291 g_object_unref ( vtl->track_bg_gc );
2292 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2294 // Ensure new track drawing heeds line thickness setting
2295 // however always have a minium of 2, as 1 pixel is really narrow
2296 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2298 if ( vtl->current_track_gc )
2299 g_object_unref ( vtl->current_track_gc );
2300 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2301 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2303 // 'newpoint' gc is exactly the same as the current track gc
2304 if ( vtl->current_track_newpoint_gc )
2305 g_object_unref ( vtl->current_track_newpoint_gc );
2306 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2307 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2309 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2311 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2312 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2314 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2315 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2316 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2318 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2320 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2323 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2325 VikTrwLayer *rv = trw_layer_new1 ( vp );
2326 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2328 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2329 /* early exit, as the rest is GUI related */
2333 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2334 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2336 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2337 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2339 trw_layer_new_track_gcs ( rv, vp );
2341 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2342 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2343 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2344 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2346 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2348 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2353 #define SMALL_ICON_SIZE 18
2355 * Can accept a null symbol, and may return null value
2357 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2359 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2360 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2361 // So needing a small icon for the treeview may need some resizing:
2362 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2363 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2367 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2369 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2371 GdkPixbuf *pixbuf = NULL;
2373 if ( track->has_color ) {
2374 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2375 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2376 // Here is some magic found to do the conversion
2377 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2378 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2379 ((track->color.green & 0xff00) << 8) |
2380 (track->color.blue & 0xff00);
2382 gdk_pixbuf_fill ( pixbuf, pixel );
2385 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
2388 g_object_unref (pixbuf);
2390 *new_iter = *((GtkTreeIter *) pass_along[1]);
2391 if ( track->is_route )
2392 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2394 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2396 if ( ! track->visible )
2397 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2400 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2402 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2404 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
2406 *new_iter = *((GtkTreeIter *) pass_along[1]);
2407 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2409 if ( ! wp->visible )
2410 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2413 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2415 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2418 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2420 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2423 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2425 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2428 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2431 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2433 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2434 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2436 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2438 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2441 if ( g_hash_table_size (vtl->routes) > 0 ) {
2442 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2444 pass_along[0] = &(vtl->routes_iter);
2445 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2447 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2449 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2452 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2453 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2455 pass_along[0] = &(vtl->waypoints_iter);
2456 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2458 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2460 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2465 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2469 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2470 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2471 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2472 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2474 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2476 return (t->visible ^= 1);
2480 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2482 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2484 return (t->visible ^= 1);
2488 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2490 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2492 return (t->visible ^= 1);
2501 * Return a property about tracks for this layer
2503 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2505 return vtl->line_thickness;
2508 // Structure to hold multiple track information for a layer
2517 * Build up layer multiple track information via updating the tooltip_tracks structure
2519 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2521 tt->length = tt->length + vik_track_get_length (tr);
2523 // Ensure times are available
2524 if ( tr->trackpoints &&
2525 vik_track_get_tp_first(tr)->has_timestamp &&
2526 vik_track_get_tp_last(tr)->has_timestamp ) {
2529 t1 = vik_track_get_tp_first(tr)->timestamp;
2530 t2 = vik_track_get_tp_last(tr)->timestamp;
2532 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2533 // Hence initialize to the first 'proper' value
2534 if ( tt->start_time == 0 )
2535 tt->start_time = t1;
2536 if ( tt->end_time == 0 )
2539 // Update find the earliest / last times
2540 if ( t1 < tt->start_time )
2541 tt->start_time = t1;
2542 if ( t2 > tt->end_time )
2545 // Keep track of total time
2546 // there maybe gaps within a track (eg segments)
2547 // but this should be generally good enough for a simple indicator
2548 tt->duration = tt->duration + (int)(t2-t1);
2553 * Generate tooltip text for the layer.
2554 * This is relatively complicated as it considers information for
2555 * no tracks, a single track or multiple tracks
2556 * (which may or may not have timing information)
2558 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2569 static gchar tmp_buf[128];
2572 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2574 // Safety check - I think these should always be valid
2575 if ( vtl->tracks && vtl->waypoints ) {
2576 tooltip_tracks tt = { 0.0, 0, 0 };
2577 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2579 GDate* gdate_start = g_date_new ();
2580 g_date_set_time_t (gdate_start, tt.start_time);
2582 GDate* gdate_end = g_date_new ();
2583 g_date_set_time_t (gdate_end, tt.end_time);
2585 if ( g_date_compare (gdate_start, gdate_end) ) {
2586 // Dates differ so print range on separate line
2587 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2588 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2589 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2592 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2593 if ( tt.start_time != 0 )
2594 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2598 if ( tt.length > 0.0 ) {
2599 gdouble len_in_units;
2601 // Setup info dependent on distance units
2602 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2603 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2604 len_in_units = VIK_METERS_TO_MILES(tt.length);
2607 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2608 len_in_units = tt.length/1000.0;
2611 // Timing information if available
2613 if ( tt.duration > 0 ) {
2614 g_snprintf (tbuf1, sizeof(tbuf1),
2615 _(" in %d:%02d hrs:mins"),
2616 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2618 g_snprintf (tbuf2, sizeof(tbuf2),
2619 _("\n%sTotal Length %.1f %s%s"),
2620 tbuf3, len_in_units, tbuf4, tbuf1);
2623 // Put together all the elements to form compact tooltip text
2624 g_snprintf (tmp_buf, sizeof(tmp_buf),
2625 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2626 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2628 g_date_free (gdate_start);
2629 g_date_free (gdate_end);
2636 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2640 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2642 // Very simple tooltip - may expand detail in the future...
2643 static gchar tmp_buf[32];
2644 g_snprintf (tmp_buf, sizeof(tmp_buf),
2646 g_hash_table_size (l->tracks));
2650 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2652 // Very simple tooltip - may expand detail in the future...
2653 static gchar tmp_buf[32];
2654 g_snprintf (tmp_buf, sizeof(tmp_buf),
2656 g_hash_table_size (l->routes));
2661 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2662 // Same tooltip for a route
2663 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2666 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2667 tr = g_hash_table_lookup ( l->tracks, sublayer );
2669 tr = g_hash_table_lookup ( l->routes, sublayer );
2672 // Could be a better way of handling strings - but this works...
2673 gchar time_buf1[20];
2674 gchar time_buf2[20];
2675 time_buf1[0] = '\0';
2676 time_buf2[0] = '\0';
2677 static gchar tmp_buf[100];
2678 // Compact info: Short date eg (11/20/99), duration and length
2679 // Hopefully these are the things that are most useful and so promoted into the tooltip
2680 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2681 // %x The preferred date representation for the current locale without the time.
2682 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2683 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2684 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2686 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2689 // Get length and consider the appropriate distance units
2690 gdouble tr_len = vik_track_get_length(tr);
2691 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2692 switch (dist_units) {
2693 case VIK_UNITS_DISTANCE_KILOMETRES:
2694 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2696 case VIK_UNITS_DISTANCE_MILES:
2697 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2706 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2708 // Very simple tooltip - may expand detail in the future...
2709 static gchar tmp_buf[32];
2710 g_snprintf (tmp_buf, sizeof(tmp_buf),
2712 g_hash_table_size (l->waypoints));
2716 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2718 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2719 // NB It's OK to return NULL
2724 return w->description;
2733 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2736 * set_statusbar_msg_info_trkpt:
2738 * Function to show track point information on the statusbar
2739 * Items displayed is controlled by the settings format code
2741 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2743 gchar *statusbar_format_code = NULL;
2744 gboolean need2free = FALSE;
2745 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2746 // Otherwise use default
2747 statusbar_format_code = g_strdup ( "KEATDN" );
2751 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2752 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2756 g_free ( statusbar_format_code );
2760 * Function to show basic waypoint information on the statusbar
2762 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2765 switch (a_vik_get_units_height ()) {
2766 case VIK_UNITS_HEIGHT_FEET:
2767 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2770 //VIK_UNITS_HEIGHT_METRES:
2771 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2775 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2776 // one can easily use the current pointer position to see this if needed
2777 gchar *lat = NULL, *lon = NULL;
2778 static struct LatLon ll;
2779 vik_coord_to_latlon (&(wpt->coord), &ll);
2780 a_coords_latlon_to_string ( &ll, &lat, &lon );
2782 // Combine parts to make overall message
2785 // Add comment if available
2786 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2788 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2789 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2796 * General layer selection function, find out which bit is selected and take appropriate action
2798 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2801 l->current_wp = NULL;
2802 l->current_wp_id = NULL;
2803 trw_layer_cancel_current_tp ( l, FALSE );
2806 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2810 case VIK_TREEVIEW_TYPE_LAYER:
2812 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2813 /* Mark for redraw */
2818 case VIK_TREEVIEW_TYPE_SUBLAYER:
2822 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2824 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2825 /* Mark for redraw */
2829 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2831 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2832 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2833 /* Mark for redraw */
2837 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2839 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2840 /* Mark for redraw */
2844 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2846 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2847 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2848 /* Mark for redraw */
2852 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2854 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2855 /* Mark for redraw */
2859 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2861 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2863 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2864 // Show some waypoint info
2865 set_statusbar_msg_info_wpt ( l, wpt );
2866 /* Mark for redraw */
2873 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2882 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2887 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2892 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2897 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2899 return l->waypoints;
2902 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
2904 return vtl->tracks_iters;
2907 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
2909 return vtl->routes_iters;
2912 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
2914 return vtl->waypoints;
2917 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2919 return ! ( g_hash_table_size ( vtl->tracks ) ||
2920 g_hash_table_size ( vtl->routes ) ||
2921 g_hash_table_size ( vtl->waypoints ) );
2924 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2926 return vtl->tracks_visible;
2929 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2931 return vtl->routes_visible;
2934 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2936 return vtl->waypoints_visible;
2940 * ATM use a case sensitive find
2941 * Finds the first one
2943 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2945 if ( wp && wp->name )
2946 if ( ! strcmp ( wp->name, name ) )
2952 * Get waypoint by name - not guaranteed to be unique
2953 * Finds the first one
2955 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2957 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2961 * ATM use a case sensitive find
2962 * Finds the first one
2964 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2966 if ( trk && trk->name )
2967 if ( ! strcmp ( trk->name, name ) )
2973 * Get track by name - not guaranteed to be unique
2974 * Finds the first one
2976 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2978 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2982 * Get route by name - not guaranteed to be unique
2983 * Finds the first one
2985 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2987 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2990 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2992 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2993 maxmin[0].lat = trk->bbox.north;
2994 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2995 maxmin[1].lat = trk->bbox.south;
2996 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2997 maxmin[0].lon = trk->bbox.east;
2998 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2999 maxmin[1].lon = trk->bbox.west;
3002 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3004 // Continually reuse maxmin to find the latest maximum and minimum values
3005 // First set to waypoints bounds
3006 maxmin[0].lat = vtl->waypoints_bbox.north;
3007 maxmin[1].lat = vtl->waypoints_bbox.south;
3008 maxmin[0].lon = vtl->waypoints_bbox.east;
3009 maxmin[1].lon = vtl->waypoints_bbox.west;
3010 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3011 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3014 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3016 /* 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... */
3017 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3018 trw_layer_find_maxmin (vtl, maxmin);
3019 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3023 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3024 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3029 static void trw_layer_centerize ( menu_array_layer values )
3031 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3033 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3034 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3036 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3039 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3041 /* First set the center [in case previously viewing from elsewhere] */
3042 /* Then loop through zoom levels until provided positions are in view */
3043 /* This method is not particularly fast - but should work well enough */
3044 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3046 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3047 vik_viewport_set_center_coord ( vvp, &coord );
3049 /* Convert into definite 'smallest' and 'largest' positions */
3050 struct LatLon minmin;
3051 if ( maxmin[0].lat < maxmin[1].lat )
3052 minmin.lat = maxmin[0].lat;
3054 minmin.lat = maxmin[1].lat;
3056 struct LatLon maxmax;
3057 if ( maxmin[0].lon > maxmin[1].lon )
3058 maxmax.lon = maxmin[0].lon;
3060 maxmax.lon = maxmin[1].lon;
3062 /* Never zoom in too far - generally not that useful, as too close ! */
3063 /* Always recalculate the 'best' zoom level */
3065 vik_viewport_set_zoom ( vvp, zoom );
3067 gdouble min_lat, max_lat, min_lon, max_lon;
3068 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3069 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3070 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3071 /* NB I think the logic used in this test to determine if the bounds is within view
3072 fails if track goes across 180 degrees longitude.
3073 Hopefully that situation is not too common...
3074 Mind you viking doesn't really do edge locations to well anyway */
3075 if ( min_lat < minmin.lat &&
3076 max_lat > minmin.lat &&
3077 min_lon < maxmax.lon &&
3078 max_lon > maxmax.lon )
3079 /* Found within zoom level */
3084 vik_viewport_set_zoom ( vvp, zoom );
3088 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3090 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3091 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3092 trw_layer_find_maxmin (vtl, maxmin);
3093 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3096 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3101 static void trw_layer_auto_view ( menu_array_layer values )
3103 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3104 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3105 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3106 vik_layers_panel_emit_update ( vlp );
3109 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3112 static void trw_layer_export ( menu_array_layer values, const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3114 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3115 GtkWidget *file_selector;
3117 gboolean failed = FALSE;
3118 file_selector = gtk_file_chooser_dialog_new (title,
3120 GTK_FILE_CHOOSER_ACTION_SAVE,
3121 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3122 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3124 gchar *cwd = g_get_current_dir();
3126 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3130 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3132 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3134 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3135 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE ||
3136 a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3138 gtk_widget_hide ( file_selector );
3139 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3140 failed = ! a_file_export ( vtl, fn, file_type, trk, TRUE );
3141 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3145 gtk_widget_destroy ( file_selector );
3147 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("The filename you requested could not be opened for writing.") );
3150 static void trw_layer_export_gpspoint ( menu_array_layer values )
3152 trw_layer_export ( values, _("Export Layer"), vik_layer_get_name(VIK_LAYER(values[MA_VTL])), NULL, FILE_TYPE_GPSPOINT );
3155 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3157 trw_layer_export ( values, _("Export Layer"), vik_layer_get_name(VIK_LAYER(values[MA_VTL])), NULL, FILE_TYPE_GPSMAPPER );
3160 static void trw_layer_export_gpx ( menu_array_layer values )
3162 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3163 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])) );
3164 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3165 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3167 trw_layer_export ( values, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3169 g_free ( auto_save_name );
3172 static void trw_layer_export_kml ( menu_array_layer values )
3174 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3175 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])) );
3176 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3177 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3179 trw_layer_export ( values, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3181 g_free ( auto_save_name );
3185 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3188 static void trw_layer_export_external_gpx ( menu_array_layer values, const gchar* external_program )
3190 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3191 gchar *name_used = NULL;
3194 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3195 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3196 gboolean failed = ! a_file_export ( vtl, name_used, FILE_TYPE_GPX, NULL, TRUE);
3197 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3199 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not create temporary file for export.") );
3203 gchar *quoted_file = g_shell_quote ( name_used );
3204 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3205 g_free ( quoted_file );
3206 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3208 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s."), external_program );
3209 g_error_free ( err );
3213 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3214 //g_remove ( name_used );
3215 // Perhaps should be deleted when the program ends?
3216 // For now leave it to the user to delete it / use system temp cleanup methods.
3217 g_free ( name_used );
3221 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3223 trw_layer_export_external_gpx ( values, a_vik_get_external_gpx_program_1() );
3226 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3228 trw_layer_export_external_gpx ( values, a_vik_get_external_gpx_program_2() );
3231 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3233 menu_array_layer data;
3234 data[MA_VTL] = values[MA_VTL];
3235 data[MA_VLP] = values[MA_VLP];
3237 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3239 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3240 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3242 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3244 if ( !trk || !trk->name )
3247 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3248 gchar *auto_save_name = g_strdup ( trk->name );
3249 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3250 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3252 gchar *label = NULL;
3253 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3254 label = _("Export Route as GPX");
3256 label = _("Export Track as GPX");
3257 trw_layer_export ( data, label, auto_save_name, trk, FILE_TYPE_GPX );
3259 g_free ( auto_save_name );
3262 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3264 wpu_udata *user_data = udata;
3265 if ( wp == user_data->wp ) {
3266 user_data->uuid = id;
3272 static void trw_layer_goto_wp ( menu_array_layer values )
3274 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3275 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3276 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3277 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3278 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3280 GTK_RESPONSE_REJECT,
3282 GTK_RESPONSE_ACCEPT,
3285 GtkWidget *label, *entry;
3286 label = gtk_label_new(_("Waypoint Name:"));
3287 entry = gtk_entry_new();
3289 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3290 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3291 gtk_widget_show_all ( label );
3292 gtk_widget_show_all ( entry );
3294 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3296 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3298 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3299 // Find *first* wp with the given name
3300 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3303 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3306 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord) );
3307 vik_layers_panel_emit_update ( vlp );
3309 // Find and select on the side panel
3314 // Hmmm, want key of it
3315 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3317 if ( wpf && udata.uuid ) {
3318 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3319 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3328 gtk_widget_destroy ( dia );
3331 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3333 gchar *default_name = highest_wp_number_get(vtl);
3334 VikWaypoint *wp = vik_waypoint_new();
3335 gchar *returned_name;
3337 wp->coord = *def_coord;
3339 // Attempt to auto set height if DEM data is available
3340 vik_waypoint_apply_dem_data ( wp, TRUE );
3342 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3344 if ( returned_name )
3347 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3348 g_free (default_name);
3349 g_free (returned_name);
3352 g_free (default_name);
3353 vik_waypoint_free(wp);
3357 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3359 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3360 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3361 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3362 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3363 VikViewport *vvp = vik_window_viewport(vw);
3365 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3366 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3367 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3368 trw_layer_calculate_bounds_waypoints ( vtl );
3369 vik_layers_panel_emit_update ( vlp );
3372 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3374 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3375 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3376 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3378 trw_layer_find_maxmin (vtl, maxmin);
3379 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3380 trw_layer_calculate_bounds_waypoints ( vtl );
3381 vik_layers_panel_emit_update ( vlp );
3384 #ifdef VIK_CONFIG_GEOTAG
3385 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3387 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3389 // Update directly - not changing the mtime
3390 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3393 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3395 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3398 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3402 * Use code in separate file for this feature as reasonably complex
3404 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3406 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3407 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3408 // Unset so can be reverified later if necessary
3409 vtl->has_verified_thumbnails = FALSE;
3411 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3417 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3419 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3420 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3422 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3428 static void trw_layer_geotagging ( menu_array_layer values )
3430 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3431 // Unset so can be reverified later if necessary
3432 vtl->has_verified_thumbnails = FALSE;
3434 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3441 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3443 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3445 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3446 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3447 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3448 VikViewport *vvp = vik_window_viewport(vw);
3450 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3454 * Acquire into this TRW Layer straight from GPS Device
3456 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3458 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3459 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3463 * Acquire into this TRW Layer from Directions
3465 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3467 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3471 * Acquire into this TRW Layer from an entered URL
3473 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3475 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3476 trw_layer_acquire ( values, &vik_datasource_url_interface );
3479 #ifdef VIK_CONFIG_OPENSTREETMAP
3481 * Acquire into this TRW Layer from OSM
3483 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3485 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3489 * Acquire into this TRW Layer from OSM for 'My' Traces
3491 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3493 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3497 #ifdef VIK_CONFIG_GEOCACHES
3499 * Acquire into this TRW Layer from Geocaching.com
3501 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3503 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3507 #ifdef VIK_CONFIG_GEOTAG
3509 * Acquire into this TRW Layer from images
3511 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3513 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3515 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3516 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3518 // Reverify thumbnails as they may have changed
3519 vtl->has_verified_thumbnails = FALSE;
3520 trw_layer_verify_thumbnails ( vtl, NULL );
3524 static void trw_layer_gps_upload ( menu_array_layer values )
3526 menu_array_sublayer data;
3528 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3530 data[MA_VTL] = values[MA_VTL];
3531 data[MA_VLP] = values[MA_VLP];
3533 trw_layer_gps_upload_any ( data );
3537 * If pass_along[3] is defined that this will upload just that track
3539 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3541 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3542 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3544 // May not actually get a track here as values[2&3] can be null
3545 VikTrack *track = NULL;
3546 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3547 gboolean xfer_all = FALSE;
3549 if ( values[MA_SUBTYPE] ) {
3551 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3552 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3555 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3556 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3559 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3562 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3566 else if ( !values[MA_CONFIRM] )
3567 xfer_all = TRUE; // i.e. whole layer
3569 if (track && !track->visible) {
3570 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3574 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3575 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3576 GTK_DIALOG_DESTROY_WITH_PARENT,
3578 GTK_RESPONSE_ACCEPT,
3580 GTK_RESPONSE_REJECT,
3583 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3584 GtkWidget *response_w = NULL;
3585 #if GTK_CHECK_VERSION (2, 20, 0)
3586 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3590 gtk_widget_grab_focus ( response_w );
3592 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3594 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3595 datasource_gps_clean_up ( dgs );
3596 gtk_widget_destroy ( dialog );
3600 // Get info from reused datasource dialog widgets
3601 gchar* protocol = datasource_gps_get_protocol ( dgs );
3602 gchar* port = datasource_gps_get_descriptor ( dgs );
3603 // NB don't free the above strings as they're references to values held elsewhere
3604 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3605 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3606 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3607 gboolean turn_off = datasource_gps_get_off ( dgs );
3609 gtk_widget_destroy ( dialog );
3611 // When called from the viewport - work the corresponding layerspanel:
3613 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3616 // Apply settings to transfer to the GPS device
3623 vik_layers_panel_get_viewport (vlp),
3632 * Acquire into this TRW Layer from any GPS Babel supported file
3634 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3636 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3637 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3638 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3639 VikViewport *vvp = vik_window_viewport(vw);
3641 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3644 static void trw_layer_new_wp ( menu_array_layer values )
3646 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3647 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3648 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3649 instead return true if you want to update. */
3650 if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) && VIK_LAYER(vtl)->visible ) {
3651 trw_layer_calculate_bounds_waypoints ( vtl );
3652 vik_layers_panel_emit_update ( vlp );
3656 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3658 vtl->current_track = vik_track_new();
3659 vik_track_set_defaults ( vtl->current_track );
3660 vtl->current_track->visible = TRUE;
3661 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3662 // Create track with the preferred colour from the layer properties
3663 vtl->current_track->color = vtl->track_color;
3665 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3666 vtl->current_track->has_color = TRUE;
3667 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3670 static void trw_layer_new_track ( menu_array_layer values )
3672 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3674 if ( ! vtl->current_track ) {
3675 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3676 new_track_create_common ( vtl, name );
3679 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3683 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3685 vtl->current_track = vik_track_new();
3686 vik_track_set_defaults ( vtl->current_track );
3687 vtl->current_track->visible = TRUE;
3688 vtl->current_track->is_route = TRUE;
3689 // By default make all routes red
3690 vtl->current_track->has_color = TRUE;
3691 gdk_color_parse ( "red", &vtl->current_track->color );
3692 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3695 static void trw_layer_new_route ( menu_array_layer values )
3697 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3699 if ( ! vtl->current_track ) {
3700 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3701 new_route_create_common ( vtl, name );
3703 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3707 static void trw_layer_auto_routes_view ( menu_array_layer values )
3709 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3710 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3712 if ( g_hash_table_size (vtl->routes) > 0 ) {
3713 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3714 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3715 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3716 vik_layers_panel_emit_update ( vlp );
3721 static void trw_layer_finish_track ( menu_array_layer values )
3723 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3724 vtl->current_track = NULL;
3725 vik_layer_emit_update ( VIK_LAYER(vtl) );
3728 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3730 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3731 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3733 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3734 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3735 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3736 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3737 vik_layers_panel_emit_update ( vlp );
3741 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3743 /* NB do not care if wp is visible or not */
3744 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3747 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3749 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3750 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3752 /* Only 1 waypoint - jump straight to it */
3753 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3754 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3755 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3757 /* If at least 2 waypoints - find center and then zoom to fit */
3758 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3760 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3761 maxmin[0].lat = vtl->waypoints_bbox.north;
3762 maxmin[1].lat = vtl->waypoints_bbox.south;
3763 maxmin[0].lon = vtl->waypoints_bbox.east;
3764 maxmin[1].lon = vtl->waypoints_bbox.west;
3765 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3768 vik_layers_panel_emit_update ( vlp );
3771 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3773 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3776 void trw_layer_osm_traces_upload_track_cb ( gpointer pass_along[8] )
3778 if ( pass_along[7] ) {
3779 VikTrack *trk = VIK_TRACK(pass_along[7]);
3780 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(pass_along[0]), trk);
3784 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3786 static menu_array_layer pass_along;
3788 GtkWidget *export_submenu;
3789 pass_along[MA_VTL] = vtl;
3790 pass_along[MA_VLP] = vlp;
3792 item = gtk_menu_item_new();
3793 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3794 gtk_widget_show ( item );
3796 if ( vtl->current_track ) {
3797 if ( vtl->current_track->is_route )
3798 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3800 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3801 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3802 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3803 gtk_widget_show ( item );
3806 item = gtk_menu_item_new ();
3807 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3808 gtk_widget_show ( item );
3811 /* Now with icons */
3812 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3815 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3816 gtk_widget_show ( item );
3818 GtkWidget *view_submenu = gtk_menu_new();
3819 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3820 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3821 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3822 gtk_widget_show ( item );
3823 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3825 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3827 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3828 gtk_widget_show ( item );
3830 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3832 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3833 gtk_widget_show ( item );
3835 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3836 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3837 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3838 gtk_widget_show ( item );
3840 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3841 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3842 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3843 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3844 gtk_widget_show ( item );
3846 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3848 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3849 gtk_widget_show ( item );
3851 export_submenu = gtk_menu_new ();
3852 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3853 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3854 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3855 gtk_widget_show ( item );
3856 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3858 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3859 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3860 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3861 gtk_widget_show ( item );
3863 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3864 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3865 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3866 gtk_widget_show ( item );
3868 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3869 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3870 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3871 gtk_widget_show ( item );
3873 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3874 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3875 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3876 gtk_widget_show ( item );
3878 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
3879 item = gtk_menu_item_new_with_mnemonic ( external1 );
3880 g_free ( external1 );
3881 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3882 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3883 gtk_widget_show ( item );
3885 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
3886 item = gtk_menu_item_new_with_mnemonic ( external2 );
3887 g_free ( external2 );
3888 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3889 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3890 gtk_widget_show ( item );
3892 GtkWidget *new_submenu = gtk_menu_new();
3893 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3895 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3896 gtk_widget_show(item);
3897 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3899 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3902 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3903 gtk_widget_show ( item );
3905 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3906 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3908 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3909 gtk_widget_show ( item );
3910 // Make it available only when a new track *not* already in progress
3911 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3913 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3915 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3916 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3917 gtk_widget_show ( item );
3918 // Make it available only when a new track *not* already in progress
3919 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3921 #ifdef VIK_CONFIG_GEOTAG
3922 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3924 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3925 gtk_widget_show ( item );
3928 GtkWidget *acquire_submenu = gtk_menu_new ();
3929 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3930 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3931 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3932 gtk_widget_show ( item );
3933 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3935 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3937 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3938 gtk_widget_show ( item );
3940 /* FIXME: only add menu when at least a routing engine has support for Directions */
3941 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3942 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3943 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3944 gtk_widget_show ( item );
3946 #ifdef VIK_CONFIG_OPENSTREETMAP
3947 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3948 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3949 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3950 gtk_widget_show ( item );
3952 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3954 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3955 gtk_widget_show ( item );
3958 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
3959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
3960 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3961 gtk_widget_show ( item );
3963 #ifdef VIK_CONFIG_GEONAMES
3964 GtkWidget *wikipedia_submenu = gtk_menu_new();
3965 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3966 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3967 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3968 gtk_widget_show(item);
3969 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3971 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3972 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3974 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3975 gtk_widget_show ( item );
3977 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3980 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3981 gtk_widget_show ( item );
3984 #ifdef VIK_CONFIG_GEOCACHES
3985 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3986 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3987 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3988 gtk_widget_show ( item );
3991 #ifdef VIK_CONFIG_GEOTAG
3992 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3994 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3995 gtk_widget_show ( item );
3998 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4000 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4001 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4002 gtk_widget_show ( item );
4004 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4006 GtkWidget *upload_submenu = gtk_menu_new ();
4007 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4009 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4010 gtk_widget_show ( item );
4011 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4013 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4014 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4016 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4017 gtk_widget_show ( item );
4019 #ifdef VIK_CONFIG_OPENSTREETMAP
4020 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4021 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4023 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4024 gtk_widget_show ( item );
4027 GtkWidget *delete_submenu = gtk_menu_new ();
4028 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4029 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4030 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4031 gtk_widget_show ( item );
4032 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4034 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4035 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4037 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4038 gtk_widget_show ( item );
4040 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4043 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4044 gtk_widget_show ( item );
4046 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4047 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4049 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4050 gtk_widget_show ( item );
4052 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4053 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4055 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4056 gtk_widget_show ( item );
4058 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4059 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4061 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4062 gtk_widget_show ( item );
4064 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4065 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4067 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4068 gtk_widget_show ( item );
4070 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4071 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4073 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4074 gtk_widget_show ( item );
4077 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4078 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4080 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4081 gtk_widget_show ( item );
4084 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4087 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4088 gtk_widget_show ( item );
4089 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4091 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4092 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4094 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4095 gtk_widget_show ( item );
4096 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4099 // Fake Waypoint UUIDs vi simple increasing integer
4100 static guint wp_uuid = 0;
4102 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4106 vik_waypoint_set_name (wp, name);
4108 if ( VIK_LAYER(vtl)->realized )
4110 // Do we need to create the sublayer:
4111 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4112 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4115 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4117 // Visibility column always needed for waypoints
4118 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
4120 // Actual setting of visibility dependent on the waypoint
4121 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4123 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4125 // Sort now as post_read is not called on a realized waypoint
4126 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4129 highest_wp_number_add_wp(vtl, name);
4130 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4134 // Fake Track UUIDs vi simple increasing integer
4135 static guint tr_uuid = 0;
4137 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4141 vik_track_set_name (t, name);
4143 if ( VIK_LAYER(vtl)->realized )
4145 // Do we need to create the sublayer:
4146 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4147 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4150 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4151 // Visibility column always needed for tracks
4152 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
4154 // Actual setting of visibility dependent on the track
4155 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4157 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4159 // Sort now as post_read is not called on a realized track
4160 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4163 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4165 trw_layer_update_treeview ( vtl, t );
4168 // Fake Route UUIDs vi simple increasing integer
4169 static guint rt_uuid = 0;
4171 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4175 vik_track_set_name (t, name);
4177 if ( VIK_LAYER(vtl)->realized )
4179 // Do we need to create the sublayer:
4180 if ( g_hash_table_size (vtl->routes) == 0 ) {
4181 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4184 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4185 // Visibility column always needed for routes
4186 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
4187 // Actual setting of visibility dependent on the route
4188 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4190 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4192 // Sort now as post_read is not called on a realized route
4193 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4196 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4198 trw_layer_update_treeview ( vtl, t );
4201 /* to be called whenever a track has been deleted or may have been changed. */
4202 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4204 if (vtl->current_tp_track == trk )
4205 trw_layer_cancel_current_tp ( vtl, FALSE );
4209 * Normally this is done to due the waypoint size preference having changed
4211 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4213 GHashTableIter iter;
4214 gpointer key, value;
4217 g_hash_table_iter_init ( &iter, vtl->waypoints );
4218 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4219 VikWaypoint *wp = VIK_WAYPOINT(value);
4221 // Reapply symbol setting to update the pixbuf
4222 gchar *tmp_symbol = g_strdup ( wp->symbol );
4223 vik_waypoint_set_symbol ( wp, tmp_symbol );
4224 g_free ( tmp_symbol );
4230 * trw_layer_new_unique_sublayer_name:
4232 * Allocates a unique new name
4234 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4237 gchar *newname = g_strdup(name);
4242 switch ( sublayer_type ) {
4243 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4244 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4246 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4247 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4250 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4253 // If found a name already in use try adding 1 to it and we try again
4255 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4257 newname = new_newname;
4260 } while ( id != NULL);
4265 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4267 // No more uniqueness of name forced when loading from a file
4268 // This now makes this function a little redunant as we just flow the parameters through
4269 vik_trw_layer_add_waypoint ( vtl, name, wp );
4272 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4274 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4275 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4276 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4277 vik_track_free ( tr );
4278 vtl->route_finder_append = FALSE; /* this means we have added it */
4281 // No more uniqueness of name forced when loading from a file
4283 vik_trw_layer_add_route ( vtl, name, tr );
4285 vik_trw_layer_add_track ( vtl, name, tr );
4287 if ( vtl->route_finder_check_added_track ) {
4288 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4289 vtl->route_finder_added_track = tr;
4294 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4296 *l = g_list_append(*l, id);
4300 * Move an item from one TRW layer to another TRW layer
4302 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4304 // TODO reconsider strategy when moving within layer (if anything...)
4305 gboolean rename = ( vtl_src != vtl_dest );
4309 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4310 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4314 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4316 newname = g_strdup ( trk->name );
4318 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4319 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4321 vik_trw_layer_delete_track ( vtl_src, trk );
4324 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4325 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4329 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4331 newname = g_strdup ( trk->name );
4333 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4334 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4336 vik_trw_layer_delete_route ( vtl_src, trk );
4339 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4340 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4344 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4346 newname = g_strdup ( wp->name );
4348 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4349 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4351 trw_layer_delete_waypoint ( vtl_src, wp );
4353 // Recalculate bounds even if not renamed as maybe dragged between layers
4354 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4355 trw_layer_calculate_bounds_waypoints ( vtl_src );
4359 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4361 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4362 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4364 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4365 GList *items = NULL;
4368 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4369 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4371 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4372 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4374 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4375 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4380 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4381 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4382 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4383 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4385 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4392 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4393 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4397 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4399 trku_udata *user_data = udata;
4400 if ( trk == user_data->trk ) {
4401 user_data->uuid = id;
4407 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4409 gboolean was_visible = FALSE;
4411 if ( trk && trk->name ) {
4413 if ( trk == vtl->current_track ) {
4414 vtl->current_track = NULL;
4415 vtl->current_tp_track = NULL;
4416 vtl->current_tp_id = NULL;
4417 vtl->moving_tp = FALSE;
4420 was_visible = trk->visible;
4422 if ( trk == vtl->route_finder_current_track )
4423 vtl->route_finder_current_track = NULL;
4425 if ( trk == vtl->route_finder_added_track )
4426 vtl->route_finder_added_track = NULL;
4432 // Hmmm, want key of it
4433 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4435 if ( trkf && udata.uuid ) {
4436 /* could be current_tp, so we have to check */
4437 trw_layer_cancel_tps_of_track ( vtl, trk );
4439 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4442 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4443 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4444 g_hash_table_remove ( vtl->tracks, udata.uuid );
4446 // If last sublayer, then remove sublayer container
4447 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4448 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4456 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4458 gboolean was_visible = FALSE;
4460 if ( trk && trk->name ) {
4462 if ( trk == vtl->current_track ) {
4463 vtl->current_track = NULL;
4464 vtl->current_tp_track = NULL;
4465 vtl->current_tp_id = NULL;
4466 vtl->moving_tp = FALSE;
4469 was_visible = trk->visible;
4471 if ( trk == vtl->route_finder_current_track )
4472 vtl->route_finder_current_track = NULL;
4474 if ( trk == vtl->route_finder_added_track )
4475 vtl->route_finder_added_track = NULL;
4481 // Hmmm, want key of it
4482 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4484 if ( trkf && udata.uuid ) {
4485 /* could be current_tp, so we have to check */
4486 trw_layer_cancel_tps_of_track ( vtl, trk );
4488 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4491 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4492 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4493 g_hash_table_remove ( vtl->routes, udata.uuid );
4495 // If last sublayer, then remove sublayer container
4496 if ( g_hash_table_size (vtl->routes) == 0 ) {
4497 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4505 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4507 gboolean was_visible = FALSE;
4509 if ( wp && wp->name ) {
4511 if ( wp == vtl->current_wp ) {
4512 vtl->current_wp = NULL;
4513 vtl->current_wp_id = NULL;
4514 vtl->moving_wp = FALSE;
4517 was_visible = wp->visible;
4523 // Hmmm, want key of it
4524 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4526 if ( wpf && udata.uuid ) {
4527 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4530 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4531 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4533 highest_wp_number_remove_wp(vtl, wp->name);
4534 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4536 // If last sublayer, then remove sublayer container
4537 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4538 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4548 // Only for temporary use by trw_layer_delete_waypoint_by_name
4549 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4551 wpu_udata *user_data = udata;
4552 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4553 user_data->uuid = id;
4560 * Delete a waypoint by the given name
4561 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4562 * as there be multiple waypoints with the same name
4564 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4567 // Fake a waypoint with the given name
4568 udata.wp = vik_waypoint_new ();
4569 vik_waypoint_set_name (udata.wp, name);
4570 // Currently only the name is used in this waypoint find function
4573 // Hmmm, want key of it
4574 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4576 vik_waypoint_free (udata.wp);
4578 if ( wpf && udata.uuid )
4579 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4585 VikTrack *trk; // input
4586 gpointer uuid; // output
4589 // Only for temporary use by trw_layer_delete_track_by_name
4590 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4592 tpu_udata *user_data = udata;
4593 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4594 user_data->uuid = id;
4601 * Delete a track by the given name
4602 * NOTE: ATM this will delete the first encountered Track with the specified name
4603 * as there may be multiple tracks with the same name within the specified hash table
4605 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4608 // Fake a track with the given name
4609 udata.trk = vik_track_new ();
4610 vik_track_set_name (udata.trk, name);
4611 // Currently only the name is used in this waypoint find function
4614 // Hmmm, want key of it
4615 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4617 vik_track_free (udata.trk);
4619 if ( trkf && udata.uuid ) {
4620 // This could be a little better written...
4621 if ( vtl->tracks == ht_tracks )
4622 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4623 if ( vtl->routes == ht_tracks )
4624 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4631 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4633 vik_treeview_item_delete (vt, it );
4636 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4639 vtl->current_track = NULL;
4640 vtl->route_finder_current_track = NULL;
4641 vtl->route_finder_added_track = NULL;
4642 if (vtl->current_tp_track)
4643 trw_layer_cancel_current_tp(vtl, FALSE);
4645 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4646 g_hash_table_remove_all(vtl->routes_iters);
4647 g_hash_table_remove_all(vtl->routes);
4649 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4651 vik_layer_emit_update ( VIK_LAYER(vtl) );
4654 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4657 vtl->current_track = NULL;
4658 vtl->route_finder_current_track = NULL;
4659 vtl->route_finder_added_track = NULL;
4660 if (vtl->current_tp_track)
4661 trw_layer_cancel_current_tp(vtl, FALSE);
4663 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4664 g_hash_table_remove_all(vtl->tracks_iters);
4665 g_hash_table_remove_all(vtl->tracks);
4667 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4669 vik_layer_emit_update ( VIK_LAYER(vtl) );
4672 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4674 vtl->current_wp = NULL;
4675 vtl->current_wp_id = NULL;
4676 vtl->moving_wp = FALSE;
4678 highest_wp_number_reset(vtl);
4680 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4681 g_hash_table_remove_all(vtl->waypoints_iters);
4682 g_hash_table_remove_all(vtl->waypoints);
4684 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4686 vik_layer_emit_update ( VIK_LAYER(vtl) );
4689 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4691 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4692 // Get confirmation from the user
4693 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4694 _("Are you sure you want to delete all tracks in %s?"),
4695 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4696 vik_trw_layer_delete_all_tracks (vtl);
4699 static void trw_layer_delete_all_routes ( menu_array_layer values )
4701 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4702 // Get confirmation from the user
4703 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4704 _("Are you sure you want to delete all routes in %s?"),
4705 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4706 vik_trw_layer_delete_all_routes (vtl);
4709 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4711 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4712 // Get confirmation from the user
4713 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4714 _("Are you sure you want to delete all waypoints in %s?"),
4715 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4716 vik_trw_layer_delete_all_waypoints (vtl);
4719 static void trw_layer_delete_item ( menu_array_sublayer values )
4721 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4722 gboolean was_visible = FALSE;
4723 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4725 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4726 if ( wp && wp->name ) {
4727 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4728 // Get confirmation from the user
4729 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4730 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4731 _("Are you sure you want to delete the waypoint \"%s\"?"),
4734 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4737 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4739 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4740 if ( trk && trk->name ) {
4741 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4742 // Get confirmation from the user
4743 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4744 _("Are you sure you want to delete the track \"%s\"?"),
4747 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4752 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4753 if ( trk && trk->name ) {
4754 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4755 // Get confirmation from the user
4756 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4757 _("Are you sure you want to delete the route \"%s\"?"),
4760 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4764 vik_layer_emit_update ( VIK_LAYER(vtl) );
4768 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4770 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4772 vik_waypoint_set_name ( wp, new_name );
4774 // Now update the treeview as well
4779 // Need key of it for treeview update
4780 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4782 if ( wpf && udataU.uuid ) {
4783 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4786 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4787 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4793 * Maintain icon of waypoint in the treeview
4795 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4797 // update the treeview
4802 // Need key of it for treeview update
4803 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4805 if ( wpf && udataU.uuid ) {
4806 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4809 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4814 static void trw_layer_properties_item ( menu_array_sublayer values )
4816 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4817 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4819 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4821 if ( wp && wp->name )
4823 gboolean updated = FALSE;
4824 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4826 trw_layer_waypoint_rename ( vtl, wp, new_name );
4828 if ( updated && values[MA_TV_ITER] )
4829 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4831 if ( updated && VIK_LAYER(vtl)->visible )
4832 vik_layer_emit_update ( VIK_LAYER(vtl) );
4838 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4839 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4841 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4843 if ( tr && tr->name )
4845 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4856 * trw_layer_track_statistics:
4858 * Show track statistics.
4859 * ATM jump to the stats page in the properties
4860 * TODO: consider separating the stats into an individual dialog?
4862 static void trw_layer_track_statistics ( menu_array_sublayer values )
4864 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4866 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4867 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4869 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4871 if ( trk && trk->name ) {
4872 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4882 * Update the treeview of the track id - primarily to update the icon
4884 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4890 gpointer *trkf = NULL;
4891 if ( trk->is_route )
4892 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4894 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4896 if ( trkf && udata.uuid ) {
4898 GtkTreeIter *iter = NULL;
4899 if ( trk->is_route )
4900 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4902 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4905 // TODO: Make this a function
4906 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4907 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4908 ((trk->color.green & 0xff00) << 8) |
4909 (trk->color.blue & 0xff00);
4910 gdk_pixbuf_fill ( pixbuf, pixel );
4911 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4912 g_object_unref (pixbuf);
4919 Parameter 1 -> VikLayersPanel
4920 Parameter 2 -> VikLayer
4921 Parameter 3 -> VikViewport
4923 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4926 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4927 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4930 /* since vlp not set, vl & vvp should be valid instead! */
4932 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4933 vik_layer_emit_update ( VIK_LAYER(vl) );
4938 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
4940 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4942 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4943 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4945 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4947 if ( track && track->trackpoints )
4948 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
4951 static void trw_layer_goto_track_center ( menu_array_sublayer values )
4953 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4955 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4956 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4958 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4960 if ( track && track->trackpoints )
4962 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4964 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4965 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4966 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4967 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4968 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
4972 static void trw_layer_convert_track_route ( menu_array_sublayer values )
4974 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4976 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4977 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4979 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4984 // Converting a track to a route can be a bit more complicated,
4985 // so give a chance to change our minds:
4986 if ( !trk->is_route &&
4987 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4988 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4990 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4991 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4996 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4999 trk_copy->is_route = !trk_copy->is_route;
5001 // ATM can't set name to self - so must create temporary copy
5002 gchar *name = g_strdup ( trk_copy->name );
5004 // Delete old one and then add new one
5005 if ( trk->is_route ) {
5006 vik_trw_layer_delete_route ( vtl, trk );
5007 vik_trw_layer_add_track ( vtl, name, trk_copy );
5010 // Extra route conversion bits...
5011 vik_track_merge_segments ( trk_copy );
5012 vik_track_to_routepoints ( trk_copy );
5014 vik_trw_layer_delete_track ( vtl, trk );
5015 vik_trw_layer_add_route ( vtl, name, trk_copy );
5019 // Update in case color of track / route changes when moving between sublayers
5020 vik_layer_emit_update ( VIK_LAYER(vtl) );
5023 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5025 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5027 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5028 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5030 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5033 vik_track_anonymize_times ( track );
5036 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5038 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5040 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5041 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5043 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5048 vtl->current_track = track;
5049 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);
5051 if ( track->trackpoints )
5052 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5056 * extend a track using route finder
5058 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5060 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5061 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5064 if ( !track->trackpoints )
5067 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5068 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5069 vtl->route_finder_current_track = track;
5070 vtl->route_finder_started = TRUE;
5072 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5078 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5080 // If have a vlp then perform a basic test to see if any DEM info available...
5082 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5084 if ( !g_list_length(dems) ) {
5085 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5093 * apply_dem_data_common:
5095 * A common function for applying the DEM values and reporting the results.
5097 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5099 if ( !trw_layer_dem_test ( vtl, vlp ) )
5102 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5103 // Inform user how much was changed
5105 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5106 g_snprintf(str, 64, tmp_str, changed);
5107 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5110 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5112 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5114 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5115 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5117 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5120 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5123 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5125 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5127 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5128 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5130 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5133 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5139 * A common function for applying the elevation smoothing and reporting the results.
5141 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5143 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5144 // Inform user how much was changed
5146 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5147 g_snprintf(str, 64, tmp_str, changed);
5148 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5154 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5156 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5158 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5159 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5161 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5166 smooth_it ( vtl, track, FALSE );
5169 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5171 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5173 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5174 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5176 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5181 smooth_it ( vtl, track, TRUE );
5185 * Commonal helper function
5187 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5190 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5191 g_snprintf(str, 64, tmp_str, changed);
5192 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5195 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5197 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5198 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5200 if ( !trw_layer_dem_test ( vtl, vlp ) )
5204 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5206 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5208 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5212 GHashTableIter iter;
5213 gpointer key, value;
5215 g_hash_table_iter_init ( &iter, vtl->waypoints );
5216 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5217 VikWaypoint *wp = VIK_WAYPOINT(value);
5218 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5221 wp_changed_message ( vtl, changed );
5224 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5226 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5227 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5229 if ( !trw_layer_dem_test ( vtl, vlp ) )
5233 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5235 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5237 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5241 GHashTableIter iter;
5242 gpointer key, value;
5244 g_hash_table_iter_init ( &iter, vtl->waypoints );
5245 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5246 VikWaypoint *wp = VIK_WAYPOINT(value);
5247 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5250 wp_changed_message ( vtl, changed );
5253 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5255 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5257 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5258 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5260 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5264 if ( !track->trackpoints )
5266 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5269 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5271 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5273 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5274 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5276 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5281 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5284 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5287 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5289 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5291 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5292 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5294 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5299 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5302 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5305 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5307 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5309 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5310 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5312 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5317 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5320 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5324 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5326 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5328 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5330 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5331 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5333 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5335 if ( trk && trk->trackpoints )
5337 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5338 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5339 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5340 if ( values[MA_VLP] )
5341 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5343 vik_layer_emit_update ( VIK_LAYER(vtl) );
5348 * Refine the selected track/route with a routing engine.
5349 * The routing engine is selected by the user, when requestiong the job.
5351 static void trw_layer_route_refine ( menu_array_sublayer values )
5353 static gint last_engine = 0;
5354 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5357 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5358 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5360 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5362 if ( trk && trk->trackpoints )
5364 /* Check size of the route */
5365 int nb = vik_track_get_tp_count(trk);
5367 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5368 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5369 GTK_MESSAGE_WARNING,
5370 GTK_BUTTONS_OK_CANCEL,
5371 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5373 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5374 gtk_widget_destroy ( dialog );
5375 if (response != GTK_RESPONSE_OK )
5378 /* Select engine from dialog */
5379 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5380 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5381 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5383 GTK_RESPONSE_REJECT,
5385 GTK_RESPONSE_ACCEPT,
5387 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5388 gtk_widget_show_all(label);
5390 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5392 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5393 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5394 gtk_widget_show_all(combo);
5396 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5398 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5400 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5402 /* Dialog validated: retrieve selected engine and do the job */
5403 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5404 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5407 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5409 /* Force saving track */
5410 /* FIXME: remove or rename this hack */
5411 vtl->route_finder_check_added_track = TRUE;
5414 vik_routing_engine_refine (routing, vtl, trk);
5416 /* FIXME: remove or rename this hack */
5417 if ( vtl->route_finder_added_track )
5418 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5420 vtl->route_finder_added_track = NULL;
5421 vtl->route_finder_check_added_track = FALSE;
5423 vik_layer_emit_update ( VIK_LAYER(vtl) );
5425 /* Restore cursor */
5426 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5428 gtk_widget_destroy ( dialog );
5432 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5434 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5435 trw_layer_tpwin_init ( vtl );
5438 /*************************************
5439 * merge/split by time routines
5440 *************************************/
5442 /* called for each key in track hash table.
5443 * If the current track has the same time stamp type, add it to the result,
5444 * except the one pointed by "exclude".
5445 * set exclude to NULL if there is no exclude to check.
5446 * Note that the result is in reverse (for performance reasons).
5451 gboolean with_timestamps;
5453 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5455 twt_udata *user_data = udata;
5456 VikTrackpoint *p1, *p2;
5457 VikTrack *trk = VIK_TRACK(value);
5458 if (trk == (VikTrack *)user_data->exclude) {
5462 if (trk->trackpoints) {
5463 p1 = vik_track_get_tp_first(trk);
5464 p2 = vik_track_get_tp_last(trk);
5466 if ( user_data->with_timestamps ) {
5467 if (!p1->has_timestamp || !p2->has_timestamp) {
5472 // Don't add tracks with timestamps when getting non timestamp tracks
5473 if (p1->has_timestamp || p2->has_timestamp) {
5479 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5482 /* called for each key in track hash table. if original track user_data[1] is close enough
5483 * to the passed one, add it to list in user_data[0]
5485 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5488 VikTrackpoint *p1, *p2;
5489 VikTrack *trk = VIK_TRACK(value);
5491 GList **nearby_tracks = ((gpointer *)user_data)[0];
5492 GList *tpoints = ((gpointer *)user_data)[1];
5495 * detect reasons for not merging, and return
5496 * if no reason is found not to merge, then do it.
5499 // Exclude the original track from the compiled list
5500 if (trk->trackpoints == tpoints) {
5504 t1 = vik_track_get_tp_first(trk)->timestamp;
5505 t2 = vik_track_get_tp_last(trk)->timestamp;
5507 if (trk->trackpoints) {
5508 p1 = vik_track_get_tp_first(trk);
5509 p2 = vik_track_get_tp_last(trk);
5511 if (!p1->has_timestamp || !p2->has_timestamp) {
5512 //g_print("no timestamp\n");
5516 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5517 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5518 if (! (abs(t1 - p2->timestamp) < threshold ||
5520 abs(p1->timestamp - t2) < threshold)
5527 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5530 /* comparison function used to sort tracks; a and b are hash table keys */
5531 /* Not actively used - can be restored if needed
5532 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5534 GHashTable *tracks = user_data;
5537 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5538 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5540 if (t1 < t2) return -1;
5541 if (t1 > t2) return 1;
5546 /* comparison function used to sort trackpoints */
5547 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5549 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5551 if (t1 < t2) return -1;
5552 if (t1 > t2) return 1;
5557 * comparison function which can be used to sort tracks or waypoints by name
5559 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5561 const gchar* namea = (const gchar*) a;
5562 const gchar* nameb = (const gchar*) b;
5563 if ( namea == NULL || nameb == NULL)
5566 // Same sort method as used in the vik_treeview_*_alphabetize functions
5567 return strcmp ( namea, nameb );
5571 * Attempt to merge selected track with other tracks specified by the user
5572 * Tracks to merge with must be of the same 'type' as the selected track -
5573 * either all with timestamps, or all without timestamps
5575 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5577 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5578 GList *other_tracks = NULL;
5579 GHashTable *ght_tracks;
5580 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5581 ght_tracks = vtl->routes;
5583 ght_tracks = vtl->tracks;
5585 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5590 if ( !track->trackpoints )
5594 udata.result = &other_tracks;
5595 udata.exclude = track->trackpoints;
5596 // Allow merging with 'similar' time type time tracks
5597 // i.e. either those times, or those without
5598 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5600 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5601 other_tracks = g_list_reverse(other_tracks);
5603 if ( !other_tracks ) {
5604 if ( udata.with_timestamps )
5605 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5607 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5611 // Sort alphabetically for user presentation
5612 // Convert into list of names for usage with dialog function
5613 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5614 GList *other_tracks_names = NULL;
5615 GList *iter = g_list_first ( other_tracks );
5617 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5618 iter = g_list_next ( iter );
5621 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5623 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5627 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5628 g_list_free(other_tracks);
5629 g_list_free(other_tracks_names);
5634 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5635 VikTrack *merge_track;
5636 if ( track->is_route )
5637 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5639 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5642 vik_track_steal_and_append_trackpoints ( track, merge_track );
5643 if ( track->is_route )
5644 vik_trw_layer_delete_route (vtl, merge_track);
5646 vik_trw_layer_delete_track (vtl, merge_track);
5647 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5650 for (l = merge_list; l != NULL; l = g_list_next(l))
5652 g_list_free(merge_list);
5654 vik_layer_emit_update( VIK_LAYER(vtl) );
5658 // c.f. trw_layer_sorted_track_id_by_name_list
5659 // but don't add the specified track to the list (normally current track)
5660 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5662 twt_udata *user_data = udata;
5665 if (trk->trackpoints == user_data->exclude) {
5669 // Sort named list alphabetically
5670 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5674 * Join - this allows combining 'tracks' and 'track routes'
5675 * i.e. doesn't care about whether tracks have consistent timestamps
5676 * ATM can only append one track at a time to the currently selected track
5678 static void trw_layer_append_track ( menu_array_sublayer values )
5681 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5683 GHashTable *ght_tracks;
5684 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5685 ght_tracks = vtl->routes;
5687 ght_tracks = vtl->tracks;
5689 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5694 GList *other_tracks_names = NULL;
5696 // Sort alphabetically for user presentation
5697 // Convert into list of names for usage with dialog function
5698 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5700 udata.result = &other_tracks_names;
5701 udata.exclude = trk->trackpoints;
5703 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5705 // Note the limit to selecting one track only
5706 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5707 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5708 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5711 trk->is_route ? _("Append Route"): _("Append Track"),
5712 trk->is_route ? _("Select the route to append after the current route") :
5713 _("Select the track to append after the current track") );
5715 g_list_free(other_tracks_names);
5717 // It's a list, but shouldn't contain more than one other track!
5718 if ( append_list ) {
5720 for (l = append_list; l != NULL; l = g_list_next(l)) {
5721 // TODO: at present this uses the first track found by name,
5722 // which with potential multiple same named tracks may not be the one selected...
5723 VikTrack *append_track;
5724 if ( trk->is_route )
5725 append_track = vik_trw_layer_get_route ( vtl, l->data );
5727 append_track = vik_trw_layer_get_track ( vtl, l->data );
5729 if ( append_track ) {
5730 vik_track_steal_and_append_trackpoints ( trk, append_track );
5731 if ( trk->is_route )
5732 vik_trw_layer_delete_route (vtl, append_track);
5734 vik_trw_layer_delete_track (vtl, append_track);
5737 for (l = append_list; l != NULL; l = g_list_next(l))
5739 g_list_free(append_list);
5741 vik_layer_emit_update( VIK_LAYER(vtl) );
5746 * Very similar to trw_layer_append_track for joining
5747 * but this allows selection from the 'other' list
5748 * If a track is selected, then is shows routes and joins the selected one
5749 * If a route is selected, then is shows tracks and joins the selected one
5751 static void trw_layer_append_other ( menu_array_sublayer values )
5754 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5756 GHashTable *ght_mykind, *ght_others;
5757 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5758 ght_mykind = vtl->routes;
5759 ght_others = vtl->tracks;
5762 ght_mykind = vtl->tracks;
5763 ght_others = vtl->routes;
5766 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5771 GList *other_tracks_names = NULL;
5773 // Sort alphabetically for user presentation
5774 // Convert into list of names for usage with dialog function
5775 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5777 udata.result = &other_tracks_names;
5778 udata.exclude = trk->trackpoints;
5780 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5782 // Note the limit to selecting one track only
5783 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5784 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5785 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5788 trk->is_route ? _("Append Track"): _("Append Route"),
5789 trk->is_route ? _("Select the track to append after the current route") :
5790 _("Select the route to append after the current track") );
5792 g_list_free(other_tracks_names);
5794 // It's a list, but shouldn't contain more than one other track!
5795 if ( append_list ) {
5797 for (l = append_list; l != NULL; l = g_list_next(l)) {
5798 // TODO: at present this uses the first track found by name,
5799 // which with potential multiple same named tracks may not be the one selected...
5801 // Get FROM THE OTHER TYPE list
5802 VikTrack *append_track;
5803 if ( trk->is_route )
5804 append_track = vik_trw_layer_get_track ( vtl, l->data );
5806 append_track = vik_trw_layer_get_route ( vtl, l->data );
5808 if ( append_track ) {
5810 if ( !append_track->is_route &&
5811 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5812 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5814 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5815 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5816 vik_track_merge_segments ( append_track );
5817 vik_track_to_routepoints ( append_track );
5824 vik_track_steal_and_append_trackpoints ( trk, append_track );
5826 // Delete copied which is FROM THE OTHER TYPE list
5827 if ( trk->is_route )
5828 vik_trw_layer_delete_track (vtl, append_track);
5830 vik_trw_layer_delete_route (vtl, append_track);
5833 for (l = append_list; l != NULL; l = g_list_next(l))
5835 g_list_free(append_list);
5836 vik_layer_emit_update( VIK_LAYER(vtl) );
5840 /* merge by segments */
5841 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
5843 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5844 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5845 guint segments = vik_track_merge_segments ( trk );
5846 // NB currently no need to redraw as segments not actually shown on the display
5847 // However inform the user of what happened:
5849 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5850 g_snprintf(str, 64, tmp_str, segments);
5851 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5854 /* merge by time routine */
5855 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
5857 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5861 GList *tracks_with_timestamp = NULL;
5862 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5863 if (orig_trk->trackpoints &&
5864 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
5865 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5870 udata.result = &tracks_with_timestamp;
5871 udata.exclude = orig_trk->trackpoints;
5872 udata.with_timestamps = TRUE;
5873 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5874 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5876 if (!tracks_with_timestamp) {
5877 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5880 g_list_free(tracks_with_timestamp);
5882 static guint threshold_in_minutes = 1;
5883 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5884 _("Merge Threshold..."),
5885 _("Merge when time between tracks less than:"),
5886 &threshold_in_minutes)) {
5890 // keep attempting to merge all tracks until no merges within the time specified is possible
5891 gboolean attempt_merge = TRUE;
5892 GList *nearby_tracks = NULL;
5894 static gpointer params[3];
5896 while ( attempt_merge ) {
5898 // Don't try again unless tracks have changed
5899 attempt_merge = FALSE;
5901 trps = orig_trk->trackpoints;
5905 if (nearby_tracks) {
5906 g_list_free(nearby_tracks);
5907 nearby_tracks = NULL;
5910 params[0] = &nearby_tracks;
5911 params[1] = (gpointer)trps;
5912 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5914 /* get a list of adjacent-in-time tracks */
5915 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5918 GList *l = nearby_tracks;
5920 /* remove trackpoints from merged track, delete track */
5921 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5922 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5924 // Tracks have changed, therefore retry again against all the remaining tracks
5925 attempt_merge = TRUE;
5930 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5933 g_list_free(nearby_tracks);
5935 vik_layer_emit_update( VIK_LAYER(vtl) );
5939 * Split a track at the currently selected trackpoint
5941 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5943 if ( !vtl->current_tpl )
5946 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5947 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5949 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5950 GList *newglist = g_list_alloc ();
5951 newglist->prev = NULL;
5952 newglist->next = vtl->current_tpl->next;
5953 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5954 tr->trackpoints = newglist;
5956 vtl->current_tpl->next->prev = newglist; /* end old track here */
5957 vtl->current_tpl->next = NULL;
5959 // Bounds of the selected track changed due to the split
5960 vik_track_calculate_bounds ( vtl->current_tp_track );
5962 vtl->current_tpl = newglist; /* change tp to first of new track. */
5963 vtl->current_tp_track = tr;
5966 vik_trw_layer_add_route ( vtl, name, tr );
5968 vik_trw_layer_add_track ( vtl, name, tr );
5970 // Bounds of the new track created by the split
5971 vik_track_calculate_bounds ( tr );
5977 // Also need id of newly created track
5980 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5982 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5984 if ( trkf && udata.uuid )
5985 vtl->current_tp_id = udata.uuid;
5987 vtl->current_tp_id = NULL;
5989 vik_layer_emit_update(VIK_LAYER(vtl));
5995 /* split by time routine */
5996 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
5998 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5999 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6000 GList *trps = track->trackpoints;
6002 GList *newlists = NULL;
6003 GList *newtps = NULL;
6004 static guint thr = 1;
6011 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6012 _("Split Threshold..."),
6013 _("Split when time between trackpoints exceeds:"),
6018 /* iterate through trackpoints, and copy them into new lists without touching original list */
6019 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6023 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6025 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6028 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6029 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6030 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6032 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6037 if (ts - prev_ts > thr*60) {
6038 /* flush accumulated trackpoints into new list */
6039 newlists = g_list_append(newlists, g_list_reverse(newtps));
6043 /* accumulate trackpoint copies in newtps, in reverse order */
6044 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6046 iter = g_list_next(iter);
6049 newlists = g_list_append(newlists, g_list_reverse(newtps));
6052 /* put lists of trackpoints into tracks */
6054 // Only bother updating if the split results in new tracks
6055 if (g_list_length (newlists) > 1) {
6060 tr = vik_track_copy ( track, FALSE );
6061 tr->trackpoints = (GList *)(iter->data);
6063 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6064 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6065 g_free ( new_tr_name );
6066 vik_track_calculate_bounds ( tr );
6067 iter = g_list_next(iter);
6069 // Remove original track and then update the display
6070 vik_trw_layer_delete_track (vtl, track);
6071 vik_layer_emit_update(VIK_LAYER(vtl));
6073 g_list_free(newlists);
6077 * Split a track by the number of points as specified by the user
6079 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6081 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6083 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6084 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6086 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6091 // Check valid track
6092 GList *trps = track->trackpoints;
6096 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6097 _("Split Every Nth Point"),
6098 _("Split on every Nth point:"),
6099 250, // Default value as per typical limited track capacity of various GPS devices
6103 // Was a valid number returned?
6109 GList *newlists = NULL;
6110 GList *newtps = NULL;
6115 /* accumulate trackpoint copies in newtps, in reverse order */
6116 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6118 if (count >= points) {
6119 /* flush accumulated trackpoints into new list */
6120 newlists = g_list_append(newlists, g_list_reverse(newtps));
6124 iter = g_list_next(iter);
6127 // If there is a remaining chunk put that into the new split list
6128 // This may well be the whole track if no split points were encountered
6130 newlists = g_list_append(newlists, g_list_reverse(newtps));
6133 /* put lists of trackpoints into tracks */
6135 // Only bother updating if the split results in new tracks
6136 if (g_list_length (newlists) > 1) {
6141 tr = vik_track_copy ( track, FALSE );
6142 tr->trackpoints = (GList *)(iter->data);
6144 if ( track->is_route ) {
6145 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6146 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6149 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6150 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6152 g_free ( new_tr_name );
6153 vik_track_calculate_bounds ( tr );
6155 iter = g_list_next(iter);
6157 // Remove original track and then update the display
6158 if ( track->is_route )
6159 vik_trw_layer_delete_route (vtl, track);
6161 vik_trw_layer_delete_track (vtl, track);
6162 vik_layer_emit_update(VIK_LAYER(vtl));
6164 g_list_free(newlists);
6168 * Split a track at the currently selected trackpoint
6170 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6172 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6173 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6174 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6178 * Split a track by its segments
6179 * Routes do not have segments so don't call this for routes
6181 static void trw_layer_split_segments ( menu_array_sublayer values )
6183 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6184 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6191 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6194 for ( i = 0; i < ntracks; i++ ) {
6196 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6197 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6198 g_free ( new_tr_name );
6203 // Remove original track
6204 vik_trw_layer_delete_track ( vtl, trk );
6205 vik_layer_emit_update ( VIK_LAYER(vtl) );
6208 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6211 /* end of split/merge routines */
6213 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6217 // Find available adjacent trackpoint
6218 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6219 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6220 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6222 // Delete current trackpoint
6223 vik_trackpoint_free ( vtl->current_tpl->data );
6224 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6226 // Set to current to the available adjacent trackpoint
6227 vtl->current_tpl = new_tpl;
6229 if ( vtl->current_tp_track ) {
6230 vik_track_calculate_bounds ( vtl->current_tp_track );
6234 // Delete current trackpoint
6235 vik_trackpoint_free ( vtl->current_tpl->data );
6236 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6237 trw_layer_cancel_current_tp ( vtl, FALSE );
6242 * Delete the selected point
6244 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6246 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6248 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6249 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6251 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6256 if ( !vtl->current_tpl )
6259 trw_layer_trackpoint_selected_delete ( vtl, trk );
6261 // Track has been updated so update tps:
6262 trw_layer_cancel_tps_of_track ( vtl, trk );
6264 vik_layer_emit_update ( VIK_LAYER(vtl) );
6268 * Delete adjacent track points at the same position
6269 * AKA Delete Dulplicates on the Properties Window
6271 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6273 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6275 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6276 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6278 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6283 gulong removed = vik_track_remove_dup_points ( trk );
6285 // Track has been updated so update tps:
6286 trw_layer_cancel_tps_of_track ( vtl, trk );
6288 // Inform user how much was deleted as it's not obvious from the normal view
6290 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6291 g_snprintf(str, 64, tmp_str, removed);
6292 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6294 vik_layer_emit_update ( VIK_LAYER(vtl) );
6298 * Delete adjacent track points with the same timestamp
6299 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6301 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6303 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6305 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6306 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6308 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6313 gulong removed = vik_track_remove_same_time_points ( trk );
6315 // Track has been updated so update tps:
6316 trw_layer_cancel_tps_of_track ( vtl, trk );
6318 // Inform user how much was deleted as it's not obvious from the normal view
6320 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6321 g_snprintf(str, 64, tmp_str, removed);
6322 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6324 vik_layer_emit_update ( VIK_LAYER(vtl) );
6330 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6332 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6334 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6335 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6337 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6342 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6344 vik_layer_emit_update ( VIK_LAYER(vtl) );
6347 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6349 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6351 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6352 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6354 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6359 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6361 vik_layer_emit_update ( VIK_LAYER(vtl) );
6367 static void trw_layer_reverse ( menu_array_sublayer values )
6369 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6371 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6372 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6374 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6379 vik_track_reverse ( track );
6381 vik_layer_emit_update ( VIK_LAYER(vtl) );
6385 * Similar to trw_layer_enum_item, but this uses a sorted method
6388 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6390 GList **list = (GList**)udata;
6391 // *list = g_list_prepend(*all, key); //unsorted method
6392 // Sort named list alphabetically
6393 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6398 * Now Waypoint specific sort
6400 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6402 GList **list = (GList**)udata;
6403 // Sort named list alphabetically
6404 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6408 * Track specific sort
6410 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6412 GList **list = (GList**)udata;
6413 // Sort named list alphabetically
6414 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6419 gboolean has_same_track_name;
6420 const gchar *same_track_name;
6421 } same_track_name_udata;
6423 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6425 const gchar* namea = (const gchar*) aa;
6426 const gchar* nameb = (const gchar*) bb;
6429 gint result = strcmp ( namea, nameb );
6431 if ( result == 0 ) {
6432 // Found two names the same
6433 same_track_name_udata *user_data = udata;
6434 user_data->has_same_track_name = TRUE;
6435 user_data->same_track_name = namea;
6438 // Leave ordering the same
6443 * Find out if any tracks have the same name in this hash table
6445 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6447 // Sort items by name, then compare if any next to each other are the same
6449 GList *track_names = NULL;
6450 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6453 if ( ! track_names )
6456 same_track_name_udata udata;
6457 udata.has_same_track_name = FALSE;
6459 // Use sort routine to traverse list comparing items
6460 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6461 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6462 // Still no tracks...
6466 return udata.has_same_track_name;
6470 * Force unqiue track names for the track table specified
6471 * Note the panel is a required parameter to enable the update of the names displayed
6472 * Specify if on tracks or else on routes
6474 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6476 // . Search list for an instance of repeated name
6477 // . get track of this name
6478 // . create new name
6479 // . rename track & update equiv. treeview iter
6480 // . repeat until all different
6482 same_track_name_udata udata;
6484 GList *track_names = NULL;
6485 udata.has_same_track_name = FALSE;
6486 udata.same_track_name = NULL;
6488 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6491 if ( ! track_names )
6494 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6496 // Still no tracks...
6497 if ( ! dummy_list1 )
6500 while ( udata.has_same_track_name ) {
6502 // Find a track with the same name
6505 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6507 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6511 g_critical("Houston, we've had a problem.");
6512 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6513 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6518 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6519 vik_track_set_name ( trk, newname );
6525 // Need want key of it for treeview update
6526 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6528 if ( trkf && udataU.uuid ) {
6532 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6534 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6537 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6539 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6541 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6545 // Start trying to find same names again...
6547 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6548 udata.has_same_track_name = FALSE;
6549 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6551 // No tracks any more - give up searching
6552 if ( ! dummy_list2 )
6553 udata.has_same_track_name = FALSE;
6557 vik_layers_panel_emit_update ( vlp );
6560 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6562 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6565 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6566 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6567 iter = &(vtl->tracks_iter);
6568 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6570 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6571 iter = &(vtl->routes_iter);
6572 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6574 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6575 iter = &(vtl->waypoints_iter);
6576 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6580 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6583 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6585 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6588 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6589 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6590 iter = &(vtl->tracks_iter);
6591 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6593 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6594 iter = &(vtl->routes_iter);
6595 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6597 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6598 iter = &(vtl->waypoints_iter);
6599 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6603 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6609 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6611 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6614 // Ensure list of track names offered is unique
6615 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6616 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6617 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6618 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6624 // Sort list alphabetically for better presentation
6625 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6628 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6632 // Get list of items to delete from the user
6633 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6636 _("Delete Selection"),
6637 _("Select tracks to delete"));
6640 // Delete requested tracks
6641 // since specificly requested, IMHO no need for extra confirmation
6642 if ( delete_list ) {
6644 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6645 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6646 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6648 g_list_free(delete_list);
6649 vik_layer_emit_update( VIK_LAYER(vtl) );
6656 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6658 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6661 // Ensure list of track names offered is unique
6662 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6663 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6664 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6665 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6671 // Sort list alphabetically for better presentation
6672 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6675 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6679 // Get list of items to delete from the user
6680 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6683 _("Delete Selection"),
6684 _("Select routes to delete") );
6687 // Delete requested routes
6688 // since specificly requested, IMHO no need for extra confirmation
6689 if ( delete_list ) {
6691 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6692 // This deletes first route it finds of that name (but uniqueness is enforced above)
6693 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6695 g_list_free(delete_list);
6696 vik_layer_emit_update( VIK_LAYER(vtl) );
6701 gboolean has_same_waypoint_name;
6702 const gchar *same_waypoint_name;
6703 } same_waypoint_name_udata;
6705 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6707 const gchar* namea = (const gchar*) aa;
6708 const gchar* nameb = (const gchar*) bb;
6711 gint result = strcmp ( namea, nameb );
6713 if ( result == 0 ) {
6714 // Found two names the same
6715 same_waypoint_name_udata *user_data = udata;
6716 user_data->has_same_waypoint_name = TRUE;
6717 user_data->same_waypoint_name = namea;
6720 // Leave ordering the same
6725 * Find out if any waypoints have the same name in this layer
6727 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6729 // Sort items by name, then compare if any next to each other are the same
6731 GList *waypoint_names = NULL;
6732 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6735 if ( ! waypoint_names )
6738 same_waypoint_name_udata udata;
6739 udata.has_same_waypoint_name = FALSE;
6741 // Use sort routine to traverse list comparing items
6742 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6743 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6744 // Still no waypoints...
6748 return udata.has_same_waypoint_name;
6752 * Force unqiue waypoint names for this layer
6753 * Note the panel is a required parameter to enable the update of the names displayed
6755 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6757 // . Search list for an instance of repeated name
6758 // . get waypoint of this name
6759 // . create new name
6760 // . rename waypoint & update equiv. treeview iter
6761 // . repeat until all different
6763 same_waypoint_name_udata udata;
6765 GList *waypoint_names = NULL;
6766 udata.has_same_waypoint_name = FALSE;
6767 udata.same_waypoint_name = NULL;
6769 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6772 if ( ! waypoint_names )
6775 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6777 // Still no waypoints...
6778 if ( ! dummy_list1 )
6781 while ( udata.has_same_waypoint_name ) {
6783 // Find a waypoint with the same name
6784 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6788 g_critical("Houston, we've had a problem.");
6789 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6790 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6795 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6797 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6799 // Start trying to find same names again...
6800 waypoint_names = NULL;
6801 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6802 udata.has_same_waypoint_name = FALSE;
6803 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6805 // No waypoints any more - give up searching
6806 if ( ! dummy_list2 )
6807 udata.has_same_waypoint_name = FALSE;
6811 vik_layers_panel_emit_update ( vlp );
6817 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
6819 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6822 // Ensure list of waypoint names offered is unique
6823 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6824 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6825 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6826 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
6832 // Sort list alphabetically for better presentation
6833 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6835 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6839 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6841 // Get list of items to delete from the user
6842 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6845 _("Delete Selection"),
6846 _("Select waypoints to delete"));
6849 // Delete requested waypoints
6850 // since specificly requested, IMHO no need for extra confirmation
6851 if ( delete_list ) {
6853 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6854 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6855 trw_layer_delete_waypoint_by_name (vtl, l->data);
6857 g_list_free(delete_list);
6859 trw_layer_calculate_bounds_waypoints ( vtl );
6860 vik_layer_emit_update( VIK_LAYER(vtl) );
6868 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6870 vik_treeview_item_toggle_visible ( vt, it );
6876 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6878 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6884 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6886 wp->visible = GPOINTER_TO_INT (on_off);
6892 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6894 wp->visible = !wp->visible;
6900 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
6902 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6903 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6904 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6905 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6907 vik_layer_emit_update ( VIK_LAYER(vtl) );
6913 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
6915 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6916 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6917 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6918 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6920 vik_layer_emit_update ( VIK_LAYER(vtl) );
6926 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
6928 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6929 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6930 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6932 vik_layer_emit_update ( VIK_LAYER(vtl) );
6938 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6940 trk->visible = GPOINTER_TO_INT (on_off);
6946 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6948 trk->visible = !trk->visible;
6954 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
6956 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6957 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6958 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6959 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6961 vik_layer_emit_update ( VIK_LAYER(vtl) );
6967 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
6969 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6970 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6971 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6972 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6974 vik_layer_emit_update ( VIK_LAYER(vtl) );
6980 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
6982 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6983 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6984 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6986 vik_layer_emit_update ( VIK_LAYER(vtl) );
6992 static void trw_layer_routes_visibility_off ( menu_array_layer values )
6994 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6995 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6996 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6997 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6999 vik_layer_emit_update ( VIK_LAYER(vtl) );
7005 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7007 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7008 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7009 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7010 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7012 vik_layer_emit_update ( VIK_LAYER(vtl) );
7018 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7020 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7021 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7022 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7024 vik_layer_emit_update ( VIK_LAYER(vtl) );
7028 * vik_trw_layer_build_waypoint_list_t:
7030 * Helper function to construct a list of #vik_trw_waypoint_list_t
7032 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7034 GList *waypoints_and_layers = NULL;
7035 // build waypoints_and_layers list
7036 while ( waypoints ) {
7037 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7038 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7040 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7041 waypoints = g_list_next ( waypoints );
7043 return waypoints_and_layers;
7047 * trw_layer_create_waypoint_list:
7049 * Create the latest list of waypoints with the associated layer(s)
7050 * Although this will always be from a single layer here
7052 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7054 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7055 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7057 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7061 * trw_layer_analyse_close:
7063 * Stuff to do on dialog closure
7065 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7067 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7068 gtk_widget_destroy ( dialog );
7069 vtl->tracks_analysis_dialog = NULL;
7073 * vik_trw_layer_build_track_list_t:
7075 * Helper function to construct a list of #vik_trw_track_list_t
7077 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7079 GList *tracks_and_layers = NULL;
7080 // build tracks_and_layers list
7082 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7083 vtdl->trk = VIK_TRACK(tracks->data);
7085 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7086 tracks = g_list_next ( tracks );
7088 return tracks_and_layers;
7092 * trw_layer_create_track_list:
7094 * Create the latest list of tracks with the associated layer(s)
7095 * Although this will always be from a single layer here
7097 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7099 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7100 GList *tracks = NULL;
7101 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7102 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7104 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7106 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7109 static void trw_layer_tracks_stats ( menu_array_layer values )
7111 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7112 // There can only be one!
7113 if ( vtl->tracks_analysis_dialog )
7116 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7117 VIK_LAYER(vtl)->name,
7119 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7120 trw_layer_create_track_list,
7121 trw_layer_analyse_close );
7127 static void trw_layer_routes_stats ( menu_array_layer values )
7129 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7130 // There can only be one!
7131 if ( vtl->tracks_analysis_dialog )
7134 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7135 VIK_LAYER(vtl)->name,
7137 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7138 trw_layer_create_track_list,
7139 trw_layer_analyse_close );
7142 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7144 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7145 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7147 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7150 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7152 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7153 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7156 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7157 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7161 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7163 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7164 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7167 if ( !strncmp(wp->comment, "http", 4) ) {
7168 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7169 } else if ( !strncmp(wp->description, "http", 4) ) {
7170 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7174 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7176 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7178 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7180 // No actual change to the name supplied
7182 if (strcmp(newname, wp->name) == 0 )
7185 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7188 // An existing waypoint has been found with the requested name
7189 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7190 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7195 // Update WP name and refresh the treeview
7196 vik_waypoint_set_name (wp, newname);
7198 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7199 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7201 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7206 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7208 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7210 // No actual change to the name supplied
7212 if (strcmp(newname, trk->name) == 0)
7215 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7218 // An existing track has been found with the requested name
7219 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7220 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7224 // Update track name and refresh GUI parts
7225 vik_track_set_name (trk, newname);
7227 // Update any subwindows that could be displaying this track which has changed name
7228 // Only one Track Edit Window
7229 if ( l->current_tp_track == trk && l->tpwin ) {
7230 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7232 // Property Dialog of the track
7233 vik_trw_layer_propwin_update ( trk );
7235 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7236 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7238 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7243 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7245 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7247 // No actual change to the name supplied
7249 if (strcmp(newname, trk->name) == 0)
7252 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7255 // An existing track has been found with the requested name
7256 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7257 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7261 // Update track name and refresh GUI parts
7262 vik_track_set_name (trk, newname);
7264 // Update any subwindows that could be displaying this track which has changed name
7265 // Only one Track Edit Window
7266 if ( l->current_tp_track == trk && l->tpwin ) {
7267 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7269 // Property Dialog of the track
7270 vik_trw_layer_propwin_update ( trk );
7272 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7273 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7275 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7282 static gboolean is_valid_geocache_name ( gchar *str )
7284 gint len = strlen ( str );
7285 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]));
7288 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7290 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7291 a_acquire_set_filter_track ( trk );
7294 #ifdef VIK_CONFIG_GOOGLE
7295 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7297 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7298 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7301 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7303 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7305 gchar *escaped = uri_escape ( tr->comment );
7306 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7307 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7314 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7315 /* viewpoint is now available instead */
7316 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7318 static menu_array_sublayer pass_along;
7320 gboolean rv = FALSE;
7322 pass_along[MA_VTL] = l;
7323 pass_along[MA_VLP] = vlp;
7324 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7325 pass_along[MA_SUBLAYER_ID] = sublayer;
7326 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7327 pass_along[MA_VVP] = vvp;
7328 pass_along[MA_TV_ITER] = iter;
7329 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7331 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7335 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7337 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7338 gtk_widget_show ( item );
7340 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7341 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7342 if (tr && tr->property_dialog)
7343 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7345 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7346 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7347 if (tr && tr->property_dialog)
7348 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7351 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7353 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7354 gtk_widget_show ( item );
7356 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7357 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7358 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7359 gtk_widget_show ( item );
7361 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7363 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7364 gtk_widget_show ( item );
7366 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7368 // Always create separator as now there is always at least the transform menu option
7369 item = gtk_menu_item_new ();
7370 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7371 gtk_widget_show ( item );
7373 /* could be a right-click using the tool */
7374 if ( vlp != NULL ) {
7375 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7376 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7378 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7379 gtk_widget_show ( item );
7382 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7384 if ( wp && wp->name ) {
7385 if ( is_valid_geocache_name ( wp->name ) ) {
7386 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7387 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7388 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7389 gtk_widget_show ( item );
7391 #ifdef VIK_CONFIG_GEOTAG
7392 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7394 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7395 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7396 gtk_widget_show ( item );
7400 if ( wp && wp->image )
7402 // Set up image paramater
7403 pass_along[MA_MISC] = wp->image;
7405 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7406 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
7407 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7408 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7409 gtk_widget_show ( item );
7411 #ifdef VIK_CONFIG_GEOTAG
7412 GtkWidget *geotag_submenu = gtk_menu_new ();
7413 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7414 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7415 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7416 gtk_widget_show ( item );
7417 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7419 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7420 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7421 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7422 gtk_widget_show ( item );
7424 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7425 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7426 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7427 gtk_widget_show ( item );
7433 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7434 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7435 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7436 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7438 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7439 gtk_widget_show ( item );
7445 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7446 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7448 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7449 gtk_widget_show ( item );
7450 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7451 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7452 gtk_widget_set_sensitive ( item, TRUE );
7454 gtk_widget_set_sensitive ( item, FALSE );
7457 item = gtk_menu_item_new ();
7458 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7459 gtk_widget_show ( item );
7462 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7465 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7466 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7468 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7469 gtk_widget_show ( item );
7472 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7474 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7475 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7476 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7477 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7478 gtk_widget_show ( item );
7480 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7481 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7482 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7483 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7484 gtk_widget_show ( item );
7486 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7489 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7490 gtk_widget_show ( item );
7492 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7493 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7495 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7496 gtk_widget_show ( item );
7498 GtkWidget *vis_submenu = gtk_menu_new ();
7499 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7500 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7501 gtk_widget_show ( item );
7502 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7504 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7507 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7508 gtk_widget_show ( item );
7510 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7512 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7513 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7514 gtk_widget_show ( item );
7516 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7517 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7519 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7520 gtk_widget_show ( item );
7522 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7523 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7524 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7525 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7528 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7532 if ( l->current_track && !l->current_track->is_route ) {
7533 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7535 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7536 gtk_widget_show ( item );
7538 item = gtk_menu_item_new ();
7539 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7540 gtk_widget_show ( item );
7543 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7544 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7546 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7547 gtk_widget_show ( item );
7549 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7550 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7551 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7552 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7553 gtk_widget_show ( item );
7554 // Make it available only when a new track *not* already in progress
7555 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7557 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7558 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7560 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7561 gtk_widget_show ( item );
7563 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7564 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7566 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7567 gtk_widget_show ( item );
7569 GtkWidget *vis_submenu = gtk_menu_new ();
7570 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7571 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7572 gtk_widget_show ( item );
7573 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7575 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7576 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7577 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7578 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7579 gtk_widget_show ( item );
7581 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7582 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7584 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7585 gtk_widget_show ( item );
7587 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7588 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7590 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7592 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7593 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7594 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7595 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7596 gtk_widget_show ( item );
7598 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7600 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7601 gtk_widget_show ( item );
7604 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7608 if ( l->current_track && l->current_track->is_route ) {
7609 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7610 // Reuse finish track method
7611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7612 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7613 gtk_widget_show ( item );
7615 item = gtk_menu_item_new ();
7616 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7617 gtk_widget_show ( item );
7620 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7621 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7623 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7624 gtk_widget_show ( item );
7626 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7627 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7628 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7629 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7630 gtk_widget_show ( item );
7631 // Make it available only when a new track *not* already in progress
7632 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7634 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7635 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7636 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7637 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7638 gtk_widget_show ( item );
7640 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7641 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7642 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7643 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7644 gtk_widget_show ( item );
7646 GtkWidget *vis_submenu = gtk_menu_new ();
7647 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7648 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7649 gtk_widget_show ( item );
7650 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7652 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7653 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7655 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7656 gtk_widget_show ( item );
7658 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7659 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7661 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7662 gtk_widget_show ( item );
7664 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7665 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7666 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7667 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7669 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7670 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7672 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7674 gtk_widget_show ( item );
7676 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7677 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7678 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7679 gtk_widget_show ( item );
7683 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7684 GtkWidget *submenu_sort = gtk_menu_new ();
7685 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7686 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7687 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7688 gtk_widget_show ( item );
7689 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7691 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7692 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7693 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7694 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7695 gtk_widget_show ( item );
7697 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7698 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7700 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7701 gtk_widget_show ( item );
7704 GtkWidget *upload_submenu = gtk_menu_new ();
7706 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7708 item = gtk_menu_item_new ();
7709 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7710 gtk_widget_show ( item );
7712 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7713 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7714 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7715 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7716 if ( l->current_track ) {
7717 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7718 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7719 gtk_widget_show ( item );
7722 item = gtk_menu_item_new ();
7723 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7724 gtk_widget_show ( item );
7727 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7728 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7730 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7731 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7732 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7733 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7734 gtk_widget_show ( item );
7736 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7738 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7739 gtk_widget_show ( item );
7741 GtkWidget *goto_submenu;
7742 goto_submenu = gtk_menu_new ();
7743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7745 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7746 gtk_widget_show ( item );
7747 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7749 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7752 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7753 gtk_widget_show ( item );
7755 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7756 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7758 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7759 gtk_widget_show ( item );
7761 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7762 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7763 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7764 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7765 gtk_widget_show ( item );
7767 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7768 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7770 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7771 gtk_widget_show ( item );
7773 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7774 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7776 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7777 gtk_widget_show ( item );
7779 // Routes don't have speeds
7780 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7781 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7782 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7783 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7784 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7785 gtk_widget_show ( item );
7788 GtkWidget *combine_submenu;
7789 combine_submenu = gtk_menu_new ();
7790 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7791 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7792 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7793 gtk_widget_show ( item );
7794 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7796 // Routes don't have times or segments...
7797 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7798 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7800 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7801 gtk_widget_show ( item );
7803 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7805 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7806 gtk_widget_show ( item );
7809 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7811 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7812 gtk_widget_show ( item );
7814 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7815 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7817 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7820 gtk_widget_show ( item );
7822 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7823 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7825 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7827 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7828 gtk_widget_show ( item );
7830 GtkWidget *split_submenu;
7831 split_submenu = gtk_menu_new ();
7832 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7833 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7834 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7835 gtk_widget_show ( item );
7836 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7838 // Routes don't have times or segments...
7839 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7840 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7842 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7843 gtk_widget_show ( item );
7845 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7846 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7848 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7849 gtk_widget_show ( item );
7852 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7853 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7854 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7855 gtk_widget_show ( item );
7857 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7859 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7860 gtk_widget_show ( item );
7861 // Make it available only when a trackpoint is selected.
7862 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7864 GtkWidget *insert_submenu = gtk_menu_new ();
7865 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7866 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7867 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7868 gtk_widget_show ( item );
7869 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7871 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7873 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7874 gtk_widget_show ( item );
7875 // Make it available only when a point is selected
7876 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7878 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7880 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7881 gtk_widget_show ( item );
7882 // Make it available only when a point is selected
7883 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7885 GtkWidget *delete_submenu;
7886 delete_submenu = gtk_menu_new ();
7887 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7888 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7889 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7890 gtk_widget_show ( item );
7891 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7893 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7896 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7897 gtk_widget_show ( item );
7898 // Make it available only when a point is selected
7899 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7901 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7903 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7904 gtk_widget_show ( item );
7906 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7908 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7909 gtk_widget_show ( item );
7911 GtkWidget *transform_submenu;
7912 transform_submenu = gtk_menu_new ();
7913 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7915 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7916 gtk_widget_show ( item );
7917 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7919 GtkWidget *dem_submenu;
7920 dem_submenu = gtk_menu_new ();
7921 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7922 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
7923 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7924 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7926 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7927 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7928 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7929 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7930 gtk_widget_show ( item );
7932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7934 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7935 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7936 gtk_widget_show ( item );
7938 GtkWidget *smooth_submenu;
7939 smooth_submenu = gtk_menu_new ();
7940 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7941 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7942 gtk_widget_show ( item );
7943 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7945 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7947 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7948 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7949 gtk_widget_show ( item );
7951 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7952 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7953 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7954 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7955 gtk_widget_show ( item );
7957 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7958 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7960 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7961 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7963 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7964 gtk_widget_show ( item );
7966 // Routes don't have timestamps - so this is only available for tracks
7967 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7968 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7970 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7971 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7972 gtk_widget_show ( item );
7975 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7976 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7978 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7979 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7980 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7981 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7982 gtk_widget_show ( item );
7984 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7985 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7986 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7988 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7989 gtk_widget_show ( item );
7992 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7994 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7995 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7997 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7998 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
7999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8000 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8001 gtk_widget_show ( item );
8004 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8005 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8007 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8010 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8011 gtk_widget_show ( item );
8013 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8014 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8016 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8017 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8019 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8020 gtk_widget_show ( item );
8022 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8023 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8024 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
8025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8026 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8027 gtk_widget_show ( item );
8030 // ATM can't upload a single waypoint but can do waypoints to a GPS
8031 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8032 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8033 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8034 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8035 gtk_widget_show ( item );
8036 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8038 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8039 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8041 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8042 gtk_widget_show ( item );
8046 #ifdef VIK_CONFIG_GOOGLE
8047 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8049 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8053 gtk_widget_show ( item );
8057 // Some things aren't usable with routes
8058 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8059 #ifdef VIK_CONFIG_OPENSTREETMAP
8060 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8061 // Convert internal pointer into actual track for usage outside this file
8062 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
8063 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8065 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8066 gtk_widget_show ( item );
8069 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8070 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8072 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8073 gtk_widget_show ( item );
8075 /* ATM This function is only available via the layers panel, due to needing a vlp */
8077 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8078 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8079 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8081 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8082 gtk_widget_show ( item );
8086 #ifdef VIK_CONFIG_GEOTAG
8087 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8089 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8090 gtk_widget_show ( item );
8094 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8095 // Only show on viewport popmenu when a trackpoint is selected
8096 if ( ! vlp && l->current_tpl ) {
8098 item = gtk_menu_item_new ();
8099 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8100 gtk_widget_show ( item );
8102 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8103 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8104 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8105 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8106 gtk_widget_show ( item );
8110 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8111 GtkWidget *transform_submenu;
8112 transform_submenu = gtk_menu_new ();
8113 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8115 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8116 gtk_widget_show ( item );
8117 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8119 GtkWidget *dem_submenu;
8120 dem_submenu = gtk_menu_new ();
8121 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8122 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
8123 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8124 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8126 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8128 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8129 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8130 gtk_widget_show ( item );
8132 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8134 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8135 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8136 gtk_widget_show ( item );
8139 gtk_widget_show_all ( GTK_WIDGET(menu) );
8144 // TODO: Probably better to rework this track manipulation in viktrack.c
8145 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8148 if (!vtl->current_tpl)
8151 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8152 VikTrackpoint *tp_other = NULL;
8155 if (!vtl->current_tpl->prev)
8157 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8159 if (!vtl->current_tpl->next)
8161 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8164 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8167 VikTrackpoint *tp_new = vik_trackpoint_new();
8168 struct LatLon ll_current, ll_other;
8169 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8170 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8172 /* main positional interpolation */
8173 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8174 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8176 /* Now other properties that can be interpolated */
8177 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8179 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8180 /* Note here the division is applied to each part, then added
8181 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8182 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8183 tp_new->has_timestamp = TRUE;
8186 if (tp_current->speed != NAN && tp_other->speed != NAN)
8187 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8189 /* TODO - improve interpolation of course, as it may not be correct.
8190 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8191 [similar applies if value is in radians] */
8192 if (tp_current->course != NAN && tp_other->course != NAN)
8193 tp_new->course = (tp_current->course + tp_other->course)/2;
8195 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8197 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8198 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8200 // Otherwise try routes
8201 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8205 gint index = g_list_index ( trk->trackpoints, tp_current );
8209 // NB no recalculation of bounds since it is inserted between points
8210 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8215 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8221 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8225 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8227 if ( vtl->current_tpl )
8229 vtl->current_tpl = NULL;
8230 vtl->current_tp_track = NULL;
8231 vtl->current_tp_id = NULL;
8232 vik_layer_emit_update(VIK_LAYER(vtl));
8236 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8238 g_assert ( vtl->tpwin != NULL );
8239 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8240 trw_layer_cancel_current_tp ( vtl, TRUE );
8242 if ( vtl->current_tpl == NULL )
8245 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8247 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8248 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8250 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8252 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8254 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8258 trw_layer_trackpoint_selected_delete ( vtl, tr );
8260 if ( vtl->current_tpl )
8261 // Reset dialog with the available adjacent trackpoint
8262 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8264 vik_layer_emit_update(VIK_LAYER(vtl));
8266 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8268 if ( vtl->current_tp_track )
8269 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8270 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8272 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8274 if ( vtl->current_tp_track )
8275 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8276 vik_layer_emit_update(VIK_LAYER(vtl));
8278 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8280 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8281 vik_layer_emit_update(VIK_LAYER(vtl));
8283 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8284 vik_layer_emit_update(VIK_LAYER(vtl));
8288 * trw_layer_dialog_shift:
8289 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8291 * Try to reposition a dialog if it's over the specified coord
8292 * so to not obscure the item of interest
8294 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8296 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8298 // Attempt force dialog to be shown so we can find out where it is more reliably...
8299 while ( gtk_events_pending() )
8300 gtk_main_iteration ();
8302 // get parent window position & size
8303 gint win_pos_x, win_pos_y;
8304 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8306 gint win_size_x, win_size_y;
8307 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8309 // get own dialog size
8310 gint dia_size_x, dia_size_y;
8311 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8313 // get own dialog position
8314 gint dia_pos_x, dia_pos_y;
8315 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8317 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8318 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8320 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8322 gint vp_xx, vp_yy; // In viewport pixels
8323 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8325 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8329 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8331 // Transform Viewport pixels into absolute pixels
8332 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8333 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8335 // Is dialog over the point (to within an ^^ edge value)
8336 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8337 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8341 gint hh = vik_viewport_get_height ( vvp );
8343 // Consider the difference in viewport to the full window
8344 gint offset_y = dest_y;
8345 // Add difference between dialog and window sizes
8346 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8348 if ( vp_yy > hh/2 ) {
8349 // Point in bottom half, move window to top half
8350 gtk_window_move ( dialog, dia_pos_x, offset_y );
8353 // Point in top half, move dialog down
8354 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8358 // Shift left<->right
8359 gint ww = vik_viewport_get_width ( vvp );
8361 // Consider the difference in viewport to the full window
8362 gint offset_x = dest_x;
8363 // Add difference between dialog and window sizes
8364 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8366 if ( vp_xx > ww/2 ) {
8367 // Point on right, move window to left
8368 gtk_window_move ( dialog, offset_x, dia_pos_y );
8371 // Point on left, move right
8372 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8380 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8384 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8385 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8386 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8387 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8389 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8391 if ( vtl->current_tpl ) {
8392 // get tp pixel position
8393 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8395 // Shift up<->down to try not to obscure the trackpoint.
8396 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8400 if ( vtl->current_tpl )
8401 if ( vtl->current_tp_track )
8402 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8403 /* set layer name and TP data */
8406 /***************************************************************************
8408 ***************************************************************************/
8410 /*** Utility data structures and functions ****/
8414 gint closest_x, closest_y;
8415 gboolean draw_images;
8416 gpointer *closest_wp_id;
8417 VikWaypoint *closest_wp;
8423 gint closest_x, closest_y;
8424 gpointer closest_track_id;
8425 VikTrackpoint *closest_tp;
8431 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8437 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8439 // If waypoint has an image then use the image size to select
8440 if ( params->draw_images && wp->image ) {
8441 gint slackx, slacky;
8442 slackx = wp->image_width / 2;
8443 slacky = wp->image_height / 2;
8445 if ( x <= params->x + slackx && x >= params->x - slackx
8446 && y <= params->y + slacky && y >= params->y - slacky ) {
8447 params->closest_wp_id = id;
8448 params->closest_wp = wp;
8449 params->closest_x = x;
8450 params->closest_y = y;
8453 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8454 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8455 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8457 params->closest_wp_id = id;
8458 params->closest_wp = wp;
8459 params->closest_x = x;
8460 params->closest_y = y;
8464 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8466 GList *tpl = t->trackpoints;
8472 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8478 tp = VIK_TRACKPOINT(tpl->data);
8480 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8482 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8483 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8484 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8486 params->closest_track_id = id;
8487 params->closest_tp = tp;
8488 params->closest_tpl = tpl;
8489 params->closest_x = x;
8490 params->closest_y = y;
8496 // ATM: Leave this as 'Track' only.
8497 // Not overly bothered about having a snap to route trackpoint capability
8498 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8500 TPSearchParams params;
8504 params.closest_track_id = NULL;
8505 params.closest_tp = NULL;
8506 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8507 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8508 return params.closest_tp;
8511 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8513 WPSearchParams params;
8517 params.draw_images = vtl->drawimages;
8518 params.closest_wp = NULL;
8519 params.closest_wp_id = NULL;
8520 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8521 return params.closest_wp;
8525 // Some forward declarations
8526 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8527 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8528 static void marker_end_move ( tool_ed_t *t );
8531 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8535 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8537 // Here always allow snapping back to the original location
8538 // this is useful when one decides not to move the thing afterall
8539 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8542 if ( event->state & GDK_CONTROL_MASK )
8544 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8546 new_coord = tp->coord;
8550 if ( event->state & GDK_SHIFT_MASK )
8552 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8554 new_coord = wp->coord;
8558 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8560 marker_moveto ( t, x, y );
8567 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8569 if ( t->holding && event->button == 1 )
8572 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8575 if ( event->state & GDK_CONTROL_MASK )
8577 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8579 new_coord = tp->coord;
8583 if ( event->state & GDK_SHIFT_MASK )
8585 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8587 new_coord = wp->coord;
8590 marker_end_move ( t );
8592 // Determine if working on a waypoint or a trackpoint
8593 if ( t->is_waypoint ) {
8594 // Update waypoint position
8595 vtl->current_wp->coord = new_coord;
8596 trw_layer_calculate_bounds_waypoints ( vtl );
8597 // Reset waypoint pointer
8598 vtl->current_wp = NULL;
8599 vtl->current_wp_id = NULL;
8602 if ( vtl->current_tpl ) {
8603 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8605 if ( vtl->current_tp_track )
8606 vik_track_calculate_bounds ( vtl->current_tp_track );
8609 if ( vtl->current_tp_track )
8610 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8611 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8615 vik_layer_emit_update ( VIK_LAYER(vtl) );
8622 Returns true if a waypoint or track is found near the requested event position for this particular layer
8623 The item found is automatically selected
8624 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8626 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8628 if ( event->button != 1 )
8631 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8634 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8638 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8640 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8642 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8643 WPSearchParams wp_params;
8644 wp_params.vvp = vvp;
8645 wp_params.x = event->x;
8646 wp_params.y = event->y;
8647 wp_params.draw_images = vtl->drawimages;
8648 wp_params.closest_wp_id = NULL;
8649 wp_params.closest_wp = NULL;
8651 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8653 if ( wp_params.closest_wp ) {
8656 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8658 // Too easy to move it so must be holding shift to start immediately moving it
8659 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8660 if ( event->state & GDK_SHIFT_MASK ||
8661 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8662 // Put into 'move buffer'
8663 // NB vvp & vw already set in tet
8664 tet->vtl = (gpointer)vtl;
8665 tet->is_waypoint = TRUE;
8667 marker_begin_move (tet, event->x, event->y);
8670 vtl->current_wp = wp_params.closest_wp;
8671 vtl->current_wp_id = wp_params.closest_wp_id;
8673 vik_layer_emit_update ( VIK_LAYER(vtl) );
8679 // Used for both track and route lists
8680 TPSearchParams tp_params;
8681 tp_params.vvp = vvp;
8682 tp_params.x = event->x;
8683 tp_params.y = event->y;
8684 tp_params.closest_track_id = NULL;
8685 tp_params.closest_tp = NULL;
8686 tp_params.closest_tpl = NULL;
8687 tp_params.bbox = bbox;
8689 if (vtl->tracks_visible) {
8690 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8692 if ( tp_params.closest_tp ) {
8694 // Always select + highlight the track
8695 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8697 tet->is_waypoint = FALSE;
8699 // Select the Trackpoint
8700 // Can move it immediately when control held or it's the previously selected tp
8701 if ( event->state & GDK_CONTROL_MASK ||
8702 vtl->current_tpl == tp_params.closest_tpl ) {
8703 // Put into 'move buffer'
8704 // NB vvp & vw already set in tet
8705 tet->vtl = (gpointer)vtl;
8706 marker_begin_move (tet, event->x, event->y);
8709 vtl->current_tpl = tp_params.closest_tpl;
8710 vtl->current_tp_id = tp_params.closest_track_id;
8711 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8713 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8716 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8718 vik_layer_emit_update ( VIK_LAYER(vtl) );
8723 // Try again for routes
8724 if (vtl->routes_visible) {
8725 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8727 if ( tp_params.closest_tp ) {
8729 // Always select + highlight the track
8730 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8732 tet->is_waypoint = FALSE;
8734 // Select the Trackpoint
8735 // Can move it immediately when control held or it's the previously selected tp
8736 if ( event->state & GDK_CONTROL_MASK ||
8737 vtl->current_tpl == tp_params.closest_tpl ) {
8738 // Put into 'move buffer'
8739 // NB vvp & vw already set in tet
8740 tet->vtl = (gpointer)vtl;
8741 marker_begin_move (tet, event->x, event->y);
8744 vtl->current_tpl = tp_params.closest_tpl;
8745 vtl->current_tp_id = tp_params.closest_track_id;
8746 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8748 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8751 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8753 vik_layer_emit_update ( VIK_LAYER(vtl) );
8758 /* these aren't the droids you're looking for */
8759 vtl->current_wp = NULL;
8760 vtl->current_wp_id = NULL;
8761 trw_layer_cancel_current_tp ( vtl, FALSE );
8764 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8769 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8771 if ( event->button != 3 )
8774 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8777 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8780 /* Post menu for the currently selected item */
8782 /* See if a track is selected */
8783 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8784 if ( track && track->visible ) {
8786 if ( track->name ) {
8788 if ( vtl->track_right_click_menu )
8789 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8791 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8798 if ( track->is_route )
8799 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8801 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8803 if ( trkf && udataU.uuid ) {
8806 if ( track->is_route )
8807 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8809 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8811 trw_layer_sublayer_add_menu_items ( vtl,
8812 vtl->track_right_click_menu,
8814 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8820 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8826 /* See if a waypoint is selected */
8827 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8828 if ( waypoint && waypoint->visible ) {
8829 if ( waypoint->name ) {
8831 if ( vtl->wp_right_click_menu )
8832 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8834 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8837 udata.wp = waypoint;
8840 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8842 if ( wpf && udata.uuid ) {
8843 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8845 trw_layer_sublayer_add_menu_items ( vtl,
8846 vtl->wp_right_click_menu,
8848 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8853 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8862 /* background drawing hook, to be passed the viewport */
8863 static gboolean tool_sync_done = TRUE;
8865 static gboolean tool_sync(gpointer data)
8867 VikViewport *vvp = data;
8868 gdk_threads_enter();
8869 vik_viewport_sync(vvp);
8870 tool_sync_done = TRUE;
8871 gdk_threads_leave();
8875 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8878 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8879 gdk_gc_set_function ( t->gc, GDK_INVERT );
8880 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8881 vik_viewport_sync(t->vvp);
8886 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8888 VikViewport *vvp = t->vvp;
8889 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8890 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8894 if (tool_sync_done) {
8895 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8896 tool_sync_done = FALSE;
8900 static void marker_end_move ( tool_ed_t *t )
8902 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8903 g_object_unref ( t->gc );
8907 /*** Edit waypoint ****/
8909 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8911 tool_ed_t *t = g_new(tool_ed_t, 1);
8917 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8922 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8924 WPSearchParams params;
8925 tool_ed_t *t = data;
8926 VikViewport *vvp = t->vvp;
8928 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8935 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8938 if ( vtl->current_wp && vtl->current_wp->visible )
8940 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8942 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8944 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8945 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8947 if ( event->button == 3 )
8948 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8950 marker_begin_move(t, event->x, event->y);
8957 params.x = event->x;
8958 params.y = event->y;
8959 params.draw_images = vtl->drawimages;
8960 params.closest_wp_id = NULL;
8961 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8962 params.closest_wp = NULL;
8963 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8964 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8966 // how do we get here?
8967 marker_begin_move(t, event->x, event->y);
8968 g_critical("shouldn't be here");
8971 else if ( params.closest_wp )
8973 if ( event->button == 3 )
8974 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8976 vtl->waypoint_rightclick = FALSE;
8978 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8980 vtl->current_wp = params.closest_wp;
8981 vtl->current_wp_id = params.closest_wp_id;
8983 /* could make it so don't update if old WP is off screen and new is null but oh well */
8984 vik_layer_emit_update ( VIK_LAYER(vtl) );
8988 vtl->current_wp = NULL;
8989 vtl->current_wp_id = NULL;
8990 vtl->waypoint_rightclick = FALSE;
8991 vik_layer_emit_update ( VIK_LAYER(vtl) );
8995 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8997 tool_ed_t *t = data;
8998 VikViewport *vvp = t->vvp;
9000 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9005 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9008 if ( event->state & GDK_CONTROL_MASK )
9010 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9012 new_coord = tp->coord;
9016 if ( event->state & GDK_SHIFT_MASK )
9018 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9019 if ( wp && wp != vtl->current_wp )
9020 new_coord = wp->coord;
9025 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9027 marker_moveto ( t, x, y );
9034 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9036 tool_ed_t *t = data;
9037 VikViewport *vvp = t->vvp;
9039 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9042 if ( t->holding && event->button == 1 )
9045 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9048 if ( event->state & GDK_CONTROL_MASK )
9050 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9052 new_coord = tp->coord;
9056 if ( event->state & GDK_SHIFT_MASK )
9058 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9059 if ( wp && wp != vtl->current_wp )
9060 new_coord = wp->coord;
9063 marker_end_move ( t );
9065 vtl->current_wp->coord = new_coord;
9067 trw_layer_calculate_bounds_waypoints ( vtl );
9068 vik_layer_emit_update ( VIK_LAYER(vtl) );
9071 /* PUT IN RIGHT PLACE!!! */
9072 if ( event->button == 3 && vtl->waypoint_rightclick )
9074 if ( vtl->wp_right_click_menu )
9075 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9076 if ( vtl->current_wp ) {
9077 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9078 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 );
9079 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9081 vtl->waypoint_rightclick = FALSE;
9086 /*** New track ****/
9088 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9095 GdkDrawable *drawable;
9101 * Draw specified pixmap
9103 static gboolean draw_sync ( gpointer data )
9105 draw_sync_t *ds = (draw_sync_t*) data;
9106 // Sometimes don't want to draw
9107 // normally because another update has taken precedent such as panning the display
9108 // which means this pixmap is no longer valid
9109 if ( ds->vtl->draw_sync_do ) {
9110 gdk_threads_enter();
9111 gdk_draw_drawable (ds->drawable,
9114 0, 0, 0, 0, -1, -1);
9115 ds->vtl->draw_sync_done = TRUE;
9116 gdk_threads_leave();
9122 static gchar* distance_string (gdouble distance)
9126 /* draw label with distance */
9127 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9128 switch (dist_units) {
9129 case VIK_UNITS_DISTANCE_MILES:
9130 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9131 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9132 } else if (distance < 1609.4) {
9133 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9135 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9139 // VIK_UNITS_DISTANCE_KILOMETRES
9140 if (distance >= 1000 && distance < 100000) {
9141 g_sprintf(str, "%3.2f km", distance/1000.0);
9142 } else if (distance < 1000) {
9143 g_sprintf(str, "%d m", (int)distance);
9145 g_sprintf(str, "%d km", (int)distance/1000);
9149 return g_strdup (str);
9153 * Actually set the message in statusbar
9155 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9157 // Only show elevation data when track has some elevation properties
9158 gchar str_gain_loss[64];
9159 str_gain_loss[0] = '\0';
9160 gchar str_last_step[64];
9161 str_last_step[0] = '\0';
9162 gchar *str_total = distance_string (distance);
9164 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9165 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9166 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9168 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9171 if ( last_step > 0 ) {
9172 gchar *tmp = distance_string (last_step);
9173 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9177 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9179 // Write with full gain/loss information
9180 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9181 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9183 g_free ( str_total );
9187 * Figure out what information should be set in the statusbar and then write it
9189 static void update_statusbar ( VikTrwLayer *vtl )
9191 // Get elevation data
9192 gdouble elev_gain, elev_loss;
9193 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9195 /* Find out actual distance of current track */
9196 gdouble distance = vik_track_get_length (vtl->current_track);
9198 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9202 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9204 /* if we haven't sync'ed yet, we don't have time to do more. */
9205 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9206 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9208 static GdkPixmap *pixmap = NULL;
9210 // Need to check in case window has been resized
9211 w1 = vik_viewport_get_width(vvp);
9212 h1 = vik_viewport_get_height(vvp);
9214 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9216 gdk_drawable_get_size (pixmap, &w2, &h2);
9217 if (w1 != w2 || h1 != h2) {
9218 g_object_unref ( G_OBJECT ( pixmap ) );
9219 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9222 // Reset to background
9223 gdk_draw_drawable (pixmap,
9224 vtl->current_track_newpoint_gc,
9225 vik_viewport_get_pixmap(vvp),
9226 0, 0, 0, 0, -1, -1);
9228 draw_sync_t *passalong;
9231 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9233 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9234 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9235 // thus when we come to reset to the background it would include what we have already drawn!!
9236 gdk_draw_line ( pixmap,
9237 vtl->current_track_newpoint_gc,
9238 x1, y1, event->x, event->y );
9239 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9241 /* Find out actual distance of current track */
9242 gdouble distance = vik_track_get_length (vtl->current_track);
9244 // Now add distance to where the pointer is //
9247 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9248 vik_coord_to_latlon ( &coord, &ll );
9249 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9250 distance = distance + last_step;
9252 // Get elevation data
9253 gdouble elev_gain, elev_loss;
9254 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9256 // Adjust elevation data (if available) for the current pointer position
9258 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9259 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9260 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9261 // Adjust elevation of last track point
9262 if ( elev_new > last_tpt->altitude )
9264 elev_gain += elev_new - last_tpt->altitude;
9267 elev_loss += last_tpt->altitude - elev_new;
9272 // Display of the distance 'tooltip' during track creation is controlled by a preference
9274 if ( a_vik_get_create_track_tooltip() ) {
9276 gchar *str = distance_string (distance);
9278 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9279 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9280 pango_layout_set_text (pl, str, -1);
9282 pango_layout_get_pixel_size ( pl, &wd, &hd );
9285 // offset from cursor a bit depending on font size
9289 // Create a background block to make the text easier to read over the background map
9290 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9291 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9292 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9294 g_object_unref ( G_OBJECT ( pl ) );
9295 g_object_unref ( G_OBJECT ( background_block_gc ) );
9299 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9300 passalong->vtl = vtl;
9301 passalong->pixmap = pixmap;
9302 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9303 passalong->gc = vtl->current_track_newpoint_gc;
9307 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9309 // Update statusbar with full gain/loss information
9310 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9312 // draw pixmap when we have time to
9313 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9314 vtl->draw_sync_done = FALSE;
9315 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9317 return VIK_LAYER_TOOL_ACK;
9320 // NB vtl->current_track must be valid
9321 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9324 if ( vtl->current_track->trackpoints ) {
9325 // TODO rework this...
9326 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9327 GList *last = g_list_last(vtl->current_track->trackpoints);
9328 g_free ( last->data );
9329 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9331 vik_track_calculate_bounds ( vtl->current_track );
9335 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9337 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9338 vtl->current_track = NULL;
9339 vik_layer_emit_update ( VIK_LAYER(vtl) );
9341 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9342 undo_trackpoint_add ( vtl );
9343 update_statusbar ( vtl );
9344 vik_layer_emit_update ( VIK_LAYER(vtl) );
9351 * Common function to handle trackpoint button requests on either a route or a track
9352 * . enables adding a point via normal click
9353 * . enables removal of last point via right click
9354 * . finishing of the track or route via double clicking
9356 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9360 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9363 if ( event->button == 2 ) {
9364 // As the display is panning, the new track pixmap is now invalid so don't draw it
9365 // otherwise this drawing done results in flickering back to an old image
9366 vtl->draw_sync_do = FALSE;
9370 if ( event->button == 3 )
9372 if ( !vtl->current_track )
9374 undo_trackpoint_add ( vtl );
9375 update_statusbar ( vtl );
9376 vik_layer_emit_update ( VIK_LAYER(vtl) );
9380 if ( event->type == GDK_2BUTTON_PRESS )
9382 /* subtract last (duplicate from double click) tp then end */
9383 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9385 /* undo last, then end */
9386 undo_trackpoint_add ( vtl );
9387 vtl->current_track = NULL;
9389 vik_layer_emit_update ( VIK_LAYER(vtl) );
9393 tp = vik_trackpoint_new();
9394 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9396 /* snap to other TP */
9397 if ( event->state & GDK_CONTROL_MASK )
9399 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9401 tp->coord = other_tp->coord;
9404 tp->newsegment = FALSE;
9405 tp->has_timestamp = FALSE;
9408 if ( vtl->current_track ) {
9409 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9410 /* Auto attempt to get elevation from DEM data (if it's available) */
9411 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9414 vtl->ct_x1 = vtl->ct_x2;
9415 vtl->ct_y1 = vtl->ct_y2;
9416 vtl->ct_x2 = event->x;
9417 vtl->ct_y2 = event->y;
9419 vik_layer_emit_update ( VIK_LAYER(vtl) );
9423 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9425 // ----------------------------------------------------- if current is a route - switch to new track
9426 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9428 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9429 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9431 new_track_create_common ( vtl, name );
9437 return tool_new_track_or_route_click ( vtl, event, vvp );
9440 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9442 if ( event->button == 2 ) {
9443 // Pan moving ended - enable potential point drawing again
9444 vtl->draw_sync_do = TRUE;
9445 vtl->draw_sync_done = TRUE;
9449 /*** New route ****/
9451 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9456 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9458 // -------------------------- if current is a track - switch to new route
9459 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9461 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9462 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9463 new_route_create_common ( vtl, name );
9469 return tool_new_track_or_route_click ( vtl, event, vvp );
9472 /*** New waypoint ****/
9474 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9479 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9482 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9484 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9485 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9486 trw_layer_calculate_bounds_waypoints ( vtl );
9487 vik_layer_emit_update ( VIK_LAYER(vtl) );
9493 /*** Edit trackpoint ****/
9495 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9497 tool_ed_t *t = g_new(tool_ed_t, 1);
9503 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9508 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9510 tool_ed_t *t = data;
9511 VikViewport *vvp = t->vvp;
9512 TPSearchParams params;
9513 /* OUTDATED DOCUMENTATION:
9514 find 5 pixel range on each side. then put these UTM, and a pointer
9515 to the winning track name (and maybe the winning track itself), and a
9516 pointer to the winning trackpoint, inside an array or struct. pass
9517 this along, do a foreach on the tracks which will do a foreach on the
9520 params.x = event->x;
9521 params.y = event->y;
9522 params.closest_track_id = NULL;
9523 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9524 params.closest_tp = NULL;
9525 params.closest_tpl = NULL;
9526 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9528 if ( event->button != 1 )
9531 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9534 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9537 if ( vtl->current_tpl )
9539 /* 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.) */
9540 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9541 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9546 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9548 if ( current_tr->visible &&
9549 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9550 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9551 marker_begin_move ( t, event->x, event->y );
9557 if ( vtl->tracks_visible )
9558 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9560 if ( params.closest_tp )
9562 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9563 vtl->current_tpl = params.closest_tpl;
9564 vtl->current_tp_id = params.closest_track_id;
9565 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9566 trw_layer_tpwin_init ( vtl );
9567 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9568 vik_layer_emit_update ( VIK_LAYER(vtl) );
9572 if ( vtl->routes_visible )
9573 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9575 if ( params.closest_tp )
9577 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9578 vtl->current_tpl = params.closest_tpl;
9579 vtl->current_tp_id = params.closest_track_id;
9580 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9581 trw_layer_tpwin_init ( vtl );
9582 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9583 vik_layer_emit_update ( VIK_LAYER(vtl) );
9587 /* these aren't the droids you're looking for */
9591 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9593 tool_ed_t *t = data;
9594 VikViewport *vvp = t->vvp;
9596 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9602 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9605 if ( event->state & GDK_CONTROL_MASK )
9607 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9608 if ( tp && tp != vtl->current_tpl->data )
9609 new_coord = tp->coord;
9611 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9614 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9615 marker_moveto ( t, x, y );
9623 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9625 tool_ed_t *t = data;
9626 VikViewport *vvp = t->vvp;
9628 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9630 if ( event->button != 1)
9635 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9638 if ( event->state & GDK_CONTROL_MASK )
9640 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9641 if ( tp && tp != vtl->current_tpl->data )
9642 new_coord = tp->coord;
9645 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9646 if ( vtl->current_tp_track )
9647 vik_track_calculate_bounds ( vtl->current_tp_track );
9649 marker_end_move ( t );
9651 /* diff dist is diff from orig */
9653 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9655 vik_layer_emit_update ( VIK_LAYER(vtl) );
9662 /*** Route Finder ***/
9663 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9668 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9671 if ( !vtl ) return FALSE;
9672 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9673 if ( event->button == 3 && vtl->route_finder_current_track ) {
9675 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9677 vtl->route_finder_coord = *new_end;
9679 vik_layer_emit_update ( VIK_LAYER(vtl) );
9680 /* remove last ' to:...' */
9681 if ( vtl->route_finder_current_track->comment ) {
9682 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9683 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9684 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9685 last_to - vtl->route_finder_current_track->comment - 1);
9686 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9691 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9692 struct LatLon start, end;
9694 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9695 vik_coord_to_latlon ( &(tmp), &end );
9696 vtl->route_finder_coord = tmp; /* for continuations */
9698 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9699 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9700 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9702 vtl->route_finder_check_added_track = TRUE;
9703 vtl->route_finder_started = FALSE;
9706 vik_routing_default_find ( vtl, start, end);
9708 /* see if anything was done -- a track was added or appended to */
9709 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9710 vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
9711 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9712 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9713 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9714 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9717 if ( vtl->route_finder_added_track )
9718 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9720 vtl->route_finder_added_track = NULL;
9721 vtl->route_finder_check_added_track = FALSE;
9722 vtl->route_finder_append = FALSE;
9724 vik_layer_emit_update ( VIK_LAYER(vtl) );
9726 vtl->route_finder_started = TRUE;
9727 vtl->route_finder_coord = tmp;
9728 vtl->route_finder_current_track = NULL;
9733 /*** Show picture ****/
9735 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9740 /* Params are: vvp, event, last match found or NULL */
9741 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9743 if ( wp->image && wp->visible )
9745 gint x, y, slackx, slacky;
9746 GdkEventButton *event = (GdkEventButton *) params[1];
9748 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9749 slackx = wp->image_width / 2;
9750 slacky = wp->image_height / 2;
9751 if ( x <= event->x + slackx && x >= event->x - slackx
9752 && y <= event->y + slacky && y >= event->y - slacky )
9754 params[2] = wp->image; /* we've found a match. however continue searching
9755 * since we want to find the last match -- that
9756 * is, the match that was drawn last. */
9761 static void trw_layer_show_picture ( menu_array_sublayer values )
9763 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9765 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
9768 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
9769 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9770 g_free ( quoted_file );
9771 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9773 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() );
9774 g_error_free ( err );
9777 #endif /* WINDOWS */
9780 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9782 gpointer params[3] = { vvp, event, NULL };
9783 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9785 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9788 static menu_array_sublayer values;
9789 values[MA_VTL] = vtl;
9790 values[MA_MISC] = params[2];
9791 trw_layer_show_picture ( values );
9792 return TRUE; /* found a match */
9795 return FALSE; /* go through other layers, searching for a match */
9798 /***************************************************************************
9800 ***************************************************************************/
9803 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9805 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9806 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9809 /* Structure for thumbnail creating data used in the background thread */
9811 VikTrwLayer *vtl; // Layer needed for redrawing
9812 GSList *pics; // Image list
9813 } thumbnail_create_thread_data;
9815 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9817 guint total = g_slist_length(tctd->pics), done = 0;
9818 while ( tctd->pics )
9820 a_thumbnails_create ( (gchar *) tctd->pics->data );
9821 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9823 return -1; /* Abort thread */
9825 tctd->pics = tctd->pics->next;
9828 // Redraw to show the thumbnails as they are now created
9829 if ( IS_VIK_LAYER(tctd->vtl) )
9830 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9835 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9837 while ( tctd->pics )
9839 g_free ( tctd->pics->data );
9840 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9845 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9847 if ( ! vtl->has_verified_thumbnails )
9849 GSList *pics = NULL;
9850 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9853 gint len = g_slist_length ( pics );
9854 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9855 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9858 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9860 (vik_thr_func) create_thumbnails_thread,
9862 (vik_thr_free_func) thumbnail_create_thread_free,
9870 static const gchar* my_track_colors ( gint ii )
9872 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9884 // Fast and reliable way of returning a colour
9885 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9888 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9890 GHashTableIter iter;
9891 gpointer key, value;
9895 g_hash_table_iter_init ( &iter, vtl->tracks );
9897 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9899 // Tracks get a random spread of colours if not already assigned
9900 if ( ! VIK_TRACK(value)->has_color ) {
9901 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9902 VIK_TRACK(value)->color = vtl->track_color;
9904 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9906 VIK_TRACK(value)->has_color = TRUE;
9909 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9912 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9918 g_hash_table_iter_init ( &iter, vtl->routes );
9920 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9922 // Routes get an intermix of reds
9923 if ( ! VIK_TRACK(value)->has_color ) {
9925 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9927 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9928 VIK_TRACK(value)->has_color = TRUE;
9931 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9938 * (Re)Calculate the bounds of the waypoints in this layer,
9939 * This should be called whenever waypoints are changed
9941 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9943 struct LatLon topleft = { 0.0, 0.0 };
9944 struct LatLon bottomright = { 0.0, 0.0 };
9947 GHashTableIter iter;
9948 gpointer key, value;
9950 g_hash_table_iter_init ( &iter, vtl->waypoints );
9952 // Set bounds to first point
9953 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9954 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9955 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9958 // Ensure there is another point...
9959 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9961 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9963 // See if this point increases the bounds.
9964 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9966 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9967 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9968 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9969 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9973 vtl->waypoints_bbox.north = topleft.lat;
9974 vtl->waypoints_bbox.east = bottomright.lon;
9975 vtl->waypoints_bbox.south = bottomright.lat;
9976 vtl->waypoints_bbox.west = topleft.lon;
9979 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9981 vik_track_calculate_bounds ( trk );
9984 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9986 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9987 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9990 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9992 if ( ! VIK_LAYER(vtl)->vt )
9995 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9996 if ( g_hash_table_size (vtl->tracks) > 1 )
9997 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9999 if ( g_hash_table_size (vtl->routes) > 1 )
10000 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10002 if ( g_hash_table_size (vtl->waypoints) > 1 )
10003 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10006 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10008 if ( VIK_LAYER(vtl)->realized )
10009 trw_layer_verify_thumbnails ( vtl, vvp );
10010 trw_layer_track_alloc_colors ( vtl );
10012 trw_layer_calculate_bounds_waypoints ( vtl );
10013 trw_layer_calculate_bounds_tracks ( vtl );
10015 // Apply treeview sort after loading all the tracks for this layer
10016 // (rather than sorted insert on each individual track additional)
10017 // and after subsequent changes to the properties as the specified order may have changed.
10018 // since the sorting of a treeview section is now very quick
10019 // NB sorting is also performed after every name change as well to maintain the list order
10020 trw_layer_sort_all ( vtl );
10023 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10025 return vtl->coord_mode;
10029 * Uniquify the whole layer
10030 * Also requires the layers panel as the names shown there need updating too
10031 * Returns whether the operation was successful or not
10033 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10035 if ( vtl && vlp ) {
10036 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10037 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10038 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10044 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10046 vik_coord_convert ( &(wp->coord), *dest_mode );
10049 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10051 vik_track_convert ( tr, *dest_mode );
10054 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10056 if ( vtl->coord_mode != dest_mode )
10058 vtl->coord_mode = dest_mode;
10059 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10060 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10061 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10065 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10067 vtl->menu_selection = selection;
10070 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10072 return (vtl->menu_selection);
10075 /* ----------- Downloading maps along tracks --------------- */
10077 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10079 /* TODO: calculating based on current size of viewport */
10080 const gdouble w_at_zoom_0_125 = 0.0013;
10081 const gdouble h_at_zoom_0_125 = 0.0011;
10082 gdouble zoom_factor = zoom_level/0.125;
10084 wh->lat = h_at_zoom_0_125 * zoom_factor;
10085 wh->lon = w_at_zoom_0_125 * zoom_factor;
10087 return 0; /* all OK */
10090 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10092 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10093 (dist->lat >= ABS(to->north_south - from->north_south)))
10096 VikCoord *coord = g_malloc(sizeof(VikCoord));
10097 coord->mode = VIK_COORD_LATLON;
10099 if (ABS(gradient) < 1) {
10100 if (from->east_west > to->east_west)
10101 coord->east_west = from->east_west - dist->lon;
10103 coord->east_west = from->east_west + dist->lon;
10104 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10106 if (from->north_south > to->north_south)
10107 coord->north_south = from->north_south - dist->lat;
10109 coord->north_south = from->north_south + dist->lat;
10110 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10116 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10118 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10119 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10121 VikCoord *next = from;
10123 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10125 list = g_list_prepend(list, next);
10131 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10133 typedef struct _Rect {
10138 #define GLRECT(iter) ((Rect *)((iter)->data))
10141 GList *rects_to_download = NULL;
10144 if (get_download_area_width(vvp, zoom_level, &wh))
10147 GList *iter = tr->trackpoints;
10151 gboolean new_map = TRUE;
10152 VikCoord *cur_coord, tl, br;
10155 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10157 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10158 rect = g_malloc(sizeof(Rect));
10161 rect->center = *cur_coord;
10162 rects_to_download = g_list_prepend(rects_to_download, rect);
10167 gboolean found = FALSE;
10168 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10169 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10180 GList *fillins = NULL;
10181 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10182 /* seems that ATM the function get_next_coord works only for LATLON */
10183 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10184 /* fill-ins for far apart points */
10185 GList *cur_rect, *next_rect;
10186 for (cur_rect = rects_to_download;
10187 (next_rect = cur_rect->next) != NULL;
10188 cur_rect = cur_rect->next) {
10189 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10190 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10191 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10195 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10198 GList *iter = fillins;
10200 cur_coord = (VikCoord *)(iter->data);
10201 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10202 rect = g_malloc(sizeof(Rect));
10205 rect->center = *cur_coord;
10206 rects_to_download = g_list_prepend(rects_to_download, rect);
10211 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10212 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10216 for (iter = fillins; iter; iter = iter->next)
10217 g_free(iter->data);
10218 g_list_free(fillins);
10220 if (rects_to_download) {
10221 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10222 g_free(rect_iter->data);
10223 g_list_free(rects_to_download);
10227 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10231 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10232 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10233 gint selected_zoom, default_zoom;
10235 VikTrwLayer *vtl = values[MA_VTL];
10236 VikLayersPanel *vlp = values[MA_VLP];
10238 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10239 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10241 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10245 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10247 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10248 int num_maps = g_list_length(vmls);
10251 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10255 // Convert from list of vmls to list of names. Allowing the user to select one of them
10256 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10257 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10259 gchar **np = map_names;
10260 VikMapsLayer **lp = map_layers;
10262 for (i = 0; i < num_maps; i++) {
10263 vml = (VikMapsLayer *)(vmls->data);
10265 *np++ = vik_maps_layer_get_map_label(vml);
10268 // Mark end of the array lists
10272 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10273 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10274 if (cur_zoom == zoom_vals[default_zoom])
10277 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10279 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10282 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10285 for (i = 0; i < num_maps; i++)
10286 g_free(map_names[i]);
10288 g_free(map_layers);
10294 /**** lowest waypoint number calculation ***/
10295 static gint highest_wp_number_name_to_number(const gchar *name) {
10296 if ( strlen(name) == 3 ) {
10297 int n = atoi(name);
10298 if ( n < 100 && name[0] != '0' )
10300 if ( n < 10 && name[0] != '0' )
10308 static void highest_wp_number_reset(VikTrwLayer *vtl)
10310 vtl->highest_wp_number = -1;
10313 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10315 /* if is bigger that top, add it */
10316 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10317 if ( new_wp_num > vtl->highest_wp_number )
10318 vtl->highest_wp_number = new_wp_num;
10321 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10323 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10324 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10325 if ( vtl->highest_wp_number == old_wp_num ) {
10327 vtl->highest_wp_number--;
10329 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10330 /* search down until we find something that *does* exist */
10332 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10333 vtl->highest_wp_number--;
10334 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10339 /* get lowest unused number */
10340 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10343 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10345 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10346 return g_strdup(buf);
10350 * trw_layer_create_track_list_both:
10352 * Create the latest list of tracks and routes
10354 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10356 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10357 GList *tracks = NULL;
10358 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10359 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10361 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10364 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10366 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10368 gchar *title = NULL;
10369 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10370 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10372 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10374 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10378 static void trw_layer_track_list_dialog ( menu_array_layer values )
10380 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10382 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10383 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10387 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10389 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10391 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10392 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );