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 #ifdef VIK_CONFIG_GEOTAG
41 #include "viktrwlayer_geotag.h"
42 #include "geotag_exif.h"
44 #include "garminsymbols.h"
45 #include "thumbnails.h"
46 #include "background.h"
51 #include "geonamessearch.h"
52 #ifdef VIK_CONFIG_OPENSTREETMAP
53 #include "osm-traces.h"
56 #include "datasources.h"
57 #include "datasource_gps.h"
58 #include "vikexttool_datasources.h"
62 #include "vikrouting.h"
64 #include "icons/icons.h"
78 #include <gdk/gdkkeysyms.h>
80 #include <glib/gstdio.h>
81 #include <glib/gi18n.h>
83 #define VIK_TRW_LAYER_TRACK_GC 6
84 #define VIK_TRW_LAYER_TRACK_GCS 10
85 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
86 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
87 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
88 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
89 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
90 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
92 #define DRAWMODE_BY_TRACK 0
93 #define DRAWMODE_BY_SPEED 1
94 #define DRAWMODE_ALL_SAME_COLOR 2
95 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
96 // as we are (re)calculating the colour for every point
101 /* this is how it knows when you click if you are clicking close to a trackpoint. */
102 #define TRACKPOINT_SIZE_APPROX 5
103 #define WAYPOINT_SIZE_APPROX 5
105 #define MIN_STOP_LENGTH 15
106 #define MAX_STOP_LENGTH 86400
107 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
108 /* this is multiplied by user-inputted value from 1-100. */
110 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
112 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
114 FS_XX_SMALL = 0, // 'xx-small'
117 FS_MEDIUM, // DEFAULT
124 struct _VikTrwLayer {
127 GHashTable *tracks_iters;
129 GHashTable *routes_iters;
130 GHashTable *waypoints_iters;
131 GHashTable *waypoints;
132 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
133 gboolean tracks_visible, routes_visible, waypoints_visible;
134 LatLonBBox waypoints_bbox;
136 gboolean track_draw_labels;
139 guint8 drawpoints_size;
140 guint8 drawelevation;
141 guint8 elevation_factor;
145 guint8 drawdirections;
146 guint8 drawdirections_size;
147 guint8 line_thickness;
148 guint8 bg_line_thickness;
149 vik_layer_sort_order_t track_sort_order;
151 PangoLayout *tracklabellayout;
152 font_size_t track_font_size;
153 gchar *track_fsize_str;
157 gboolean wp_draw_symbols;
158 font_size_t wp_font_size;
160 vik_layer_sort_order_t wp_sort_order;
162 gdouble track_draw_speed_factor;
164 GdkGC *track_1color_gc;
165 GdkColor track_color;
166 GdkGC *current_track_gc;
167 // Separate GC for a track's potential new point as drawn via separate method
168 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
169 GdkGC *current_track_newpoint_gc;
170 GdkGC *track_bg_gc; GdkColor track_bg_color;
171 GdkGC *waypoint_gc; GdkColor waypoint_color;
172 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
173 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
175 GdkFont *waypoint_font;
176 VikTrack *current_track; // ATM shared between new tracks and new routes
177 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
178 gboolean draw_sync_done;
179 gboolean draw_sync_do;
181 VikCoordMode coord_mode;
183 /* wp editing tool */
184 VikWaypoint *current_wp;
185 gpointer current_wp_id;
187 gboolean waypoint_rightclick;
189 /* track editing tool */
191 VikTrack *current_tp_track;
192 gpointer current_tp_id;
193 VikTrwLayerTpwin *tpwin;
195 /* track editing tool -- more specifically, moving tps */
198 /* route finder tool */
199 gboolean route_finder_started;
200 VikCoord route_finder_coord;
201 gboolean route_finder_check_added_track;
202 VikTrack *route_finder_added_track;
203 VikTrack *route_finder_current_track;
204 gboolean route_finder_append;
211 guint16 image_cache_size;
213 /* for waypoint text */
214 PangoLayout *wplabellayout;
216 gboolean has_verified_thumbnails;
218 GtkMenu *wp_right_click_menu;
219 GtkMenu *track_right_click_menu;
222 VikStdLayerMenuItem menu_selection;
224 gint highest_wp_number;
227 GtkWidget *tracks_analysis_dialog;
230 /* A caached waypoint image. */
233 gchar *image; /* filename */
236 struct DrawingParams {
241 guint16 width, height;
242 gdouble cc; // Cosine factor in track directions
243 gdouble ss; // Sine factor in track directions
244 const VikCoord *center;
245 gboolean one_zone, lat_lon;
246 gdouble ce1, ce2, cn1, cn2;
250 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
252 static void trw_layer_delete_item ( gpointer pass_along[6] );
253 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
254 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
256 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
257 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
259 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
260 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
262 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
263 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
265 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl );
267 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
268 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
269 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
270 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
271 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
272 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
273 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
274 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
275 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
276 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
277 static void trw_layer_append_track ( gpointer pass_along[6] );
278 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
279 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
280 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
281 static void trw_layer_split_segments ( gpointer pass_along[6] );
282 static void trw_layer_delete_point_selected ( gpointer pass_along[6] );
283 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
284 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
285 static void trw_layer_reverse ( gpointer pass_along[6] );
286 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
287 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
288 static void trw_layer_show_picture ( gpointer pass_along[6] );
289 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
291 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
292 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
293 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
294 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
295 static void trw_layer_new_wp ( gpointer lav[2] );
296 static void trw_layer_new_track ( gpointer lav[2] );
297 static void trw_layer_new_route ( gpointer lav[2] );
298 static void trw_layer_finish_track ( gpointer lav[2] );
299 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
300 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
301 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
302 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
303 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
304 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
305 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
306 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
307 #ifdef VIK_CONFIG_GEOTAG
308 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
309 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
310 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
311 static void trw_layer_geotagging ( gpointer lav[2] );
313 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
314 static void trw_layer_acquire_routing_cb ( gpointer lav[2] );
315 #ifdef VIK_CONFIG_OPENSTREETMAP
316 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
317 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
319 #ifdef VIK_CONFIG_GEOCACHES
320 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
322 #ifdef VIK_CONFIG_GEOTAG
323 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
325 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
326 static void trw_layer_gps_upload ( gpointer lav[2] );
328 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] );
329 static void trw_layer_track_list_dialog ( gpointer lav[2] );
331 // Specific route versions:
332 // Most track handling functions can handle operating on the route list
333 // However these ones are easier in separate functions
334 static void trw_layer_auto_routes_view ( gpointer lav[2] );
335 static void trw_layer_delete_all_routes ( gpointer lav[2] );
336 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
339 static void trw_layer_properties_item ( gpointer pass_along[7] );
340 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
341 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
342 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
344 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
345 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
346 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
348 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
349 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
350 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
351 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
353 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
354 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
355 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
356 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
357 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
358 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
359 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
360 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
361 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
362 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
363 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
364 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
365 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
366 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
367 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
368 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
369 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
370 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
371 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
372 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
373 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
374 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
375 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
377 static void cached_pixbuf_free ( CachedPixbuf *cp );
378 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
380 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
381 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
383 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
384 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
386 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
387 static void highest_wp_number_reset(VikTrwLayer *vtl);
388 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
389 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
391 // Note for the following tool GtkRadioActionEntry texts:
392 // the very first text value is an internal name not displayed anywhere
393 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
394 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
395 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
396 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
397 static VikToolInterface trw_layer_tools[] = {
398 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
399 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
400 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
402 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
404 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
405 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
406 (VikToolMouseFunc) tool_new_track_click,
407 (VikToolMouseMoveFunc) tool_new_track_move,
408 (VikToolMouseFunc) tool_new_track_release,
409 (VikToolKeyFunc) tool_new_track_key_press,
410 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
411 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
413 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
414 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
415 (VikToolMouseFunc) tool_new_route_click,
416 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
417 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
418 (VikToolKeyFunc) tool_new_track_key_press, // -/#
419 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
420 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
422 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
423 (VikToolConstructorFunc) tool_edit_waypoint_create,
424 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
426 (VikToolMouseFunc) tool_edit_waypoint_click,
427 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
428 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
430 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
432 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
433 (VikToolConstructorFunc) tool_edit_trackpoint_create,
434 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
436 (VikToolMouseFunc) tool_edit_trackpoint_click,
437 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
438 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
440 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
442 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
443 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
444 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
446 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
448 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
449 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
450 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
452 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
456 TOOL_CREATE_WAYPOINT=0,
460 TOOL_EDIT_TRACKPOINT,
466 /****** PARAMETERS ******/
468 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced") };
469 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV };
471 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
472 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
474 #define MIN_POINT_SIZE 2
475 #define MAX_POINT_SIZE 10
477 #define MIN_ARROW_SIZE 3
478 #define MAX_ARROW_SIZE 20
480 static VikLayerParamScale params_scales[] = {
481 /* min max step digits */
482 { 1, 10, 1, 0 }, /* line_thickness */
483 { 0, 100, 1, 0 }, /* track draw speed factor */
484 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
485 /* 5 * step == how much to turn */
486 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
487 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
488 { 5, 500, 5, 0 }, // 5: image cache_size - " "
489 { 0, 8, 1, 0 }, // 6: Background line thickness
490 { 1, 64, 1, 0 }, /* wpsize */
491 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
492 { 1, 100, 1, 0 }, // 9: elevation factor
493 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
494 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
497 static gchar* params_font_sizes[] = {
498 N_("Extra Extra Small"),
504 N_("Extra Extra Large"),
507 // Needs to align with vik_layer_sort_order_t
508 static gchar* params_sort_order[] = {
510 N_("Name Ascending"),
511 N_("Name Descending"),
515 static VikLayerParamData black_color_default ( void ) {
516 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
518 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
519 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
520 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
521 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
522 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
523 static VikLayerParamData trackbgcolor_default ( void ) {
524 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
526 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
527 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
528 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
530 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
531 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
532 static VikLayerParamData wptextcolor_default ( void ) {
533 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
535 static VikLayerParamData wpbgcolor_default ( void ) {
536 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
538 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
539 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
541 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
542 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
543 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
545 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
547 VikLayerParam trw_layer_params[] = {
548 { 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 },
549 { 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 },
550 { 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 },
552 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
553 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
554 { 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 },
555 { 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 },
556 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
557 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
558 { 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 },
559 { 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 },
560 { 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 },
561 { 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 },
562 { 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 },
563 { 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 },
564 { 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 },
565 { 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 },
566 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
567 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 },
568 { 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 },
570 { 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 },
571 { 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 },
572 { 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,
573 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
574 { 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 },
576 { 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 },
577 { 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 },
578 { 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 },
579 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
580 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
581 { 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 },
582 { 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 },
583 { 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 },
584 { 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 },
585 { 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 },
587 { 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 },
588 { 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 },
589 { 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 },
590 { 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 },
593 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
595 // Sublayer visibilities
638 *** 1) Add to trw_layer_params and enumeration
639 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
642 /****** END PARAMETERS ******/
644 /* Layer Interface function definitions */
645 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
646 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
647 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
648 static void trw_layer_free ( VikTrwLayer *trwlayer );
649 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
650 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
651 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
652 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
653 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
654 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
655 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
656 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
657 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
658 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
659 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
660 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
661 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
662 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
663 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
664 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
665 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
666 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
667 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
668 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
669 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
670 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
671 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
672 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
673 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
674 /* End Layer Interface function definitions */
676 VikLayerInterface vik_trw_layer_interface = {
683 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
687 params_groups, /* params_groups */
688 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
692 (VikLayerFuncCreate) trw_layer_create,
693 (VikLayerFuncRealize) trw_layer_realize,
694 (VikLayerFuncPostRead) trw_layer_post_read,
695 (VikLayerFuncFree) trw_layer_free,
697 (VikLayerFuncProperties) NULL,
698 (VikLayerFuncDraw) trw_layer_draw,
699 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
701 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
702 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
704 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
705 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
707 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
708 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
709 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
710 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
711 (VikLayerFuncLayerSelected) trw_layer_selected,
713 (VikLayerFuncMarshall) trw_layer_marshall,
714 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
716 (VikLayerFuncSetParam) trw_layer_set_param,
717 (VikLayerFuncGetParam) trw_layer_get_param,
719 (VikLayerFuncReadFileData) a_gpspoint_read_file,
720 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
722 (VikLayerFuncDeleteItem) trw_layer_del_item,
723 (VikLayerFuncCutItem) trw_layer_cut_item,
724 (VikLayerFuncCopyItem) trw_layer_copy_item,
725 (VikLayerFuncPasteItem) trw_layer_paste_item,
726 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
728 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
730 (VikLayerFuncSelectClick) trw_layer_select_click,
731 (VikLayerFuncSelectMove) trw_layer_select_move,
732 (VikLayerFuncSelectRelease) trw_layer_select_release,
733 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
736 GType vik_trw_layer_get_type ()
738 static GType vtl_type = 0;
742 static const GTypeInfo vtl_info =
744 sizeof (VikTrwLayerClass),
745 NULL, /* base_init */
746 NULL, /* base_finalize */
747 NULL, /* class init */
748 NULL, /* class_finalize */
749 NULL, /* class_data */
750 sizeof (VikTrwLayer),
752 NULL /* instance init */
754 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
760 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
762 static gpointer pass_along[6];
768 pass_along[1] = NULL;
769 pass_along[2] = GINT_TO_POINTER (subtype);
770 pass_along[3] = sublayer;
771 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
772 pass_along[5] = NULL;
774 trw_layer_delete_item ( pass_along );
777 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
779 static gpointer pass_along[6];
785 pass_along[1] = NULL;
786 pass_along[2] = GINT_TO_POINTER (subtype);
787 pass_along[3] = sublayer;
788 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
789 pass_along[5] = NULL;
791 trw_layer_copy_item_cb(pass_along);
792 trw_layer_cut_item_cb(pass_along);
795 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
797 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
798 gint subtype = GPOINTER_TO_INT (pass_along[2]);
799 gpointer * sublayer = pass_along[3];
803 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
807 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
808 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
809 if ( wp && wp->name )
812 name = NULL; // Broken :(
814 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
815 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
816 if ( trk && trk->name )
819 name = NULL; // Broken :(
822 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
823 if ( trk && trk->name )
826 name = NULL; // Broken :(
829 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
830 subtype, len, name, data);
834 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
836 trw_layer_copy_item_cb(pass_along);
837 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
838 trw_layer_delete_item(pass_along);
841 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
843 // Slightly cheating method, routing via the panels capability
844 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
847 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
857 GByteArray *ba = g_byte_array_new ();
859 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
860 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
861 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
862 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
864 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
867 g_byte_array_append ( ba, id, il );
875 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
882 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
886 w = vik_waypoint_unmarshall ( item, len );
887 // When copying - we'll create a new name based on the original
888 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
889 vik_trw_layer_add_waypoint ( vtl, name, w );
890 waypoint_convert (NULL, w, &vtl->coord_mode);
893 trw_layer_calculate_bounds_waypoints ( vtl );
895 // Consider if redraw necessary for the new item
896 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
897 vik_layer_emit_update ( VIK_LAYER(vtl) );
900 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
904 t = vik_track_unmarshall ( item, len );
905 // When copying - we'll create a new name based on the original
906 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
907 vik_trw_layer_add_track ( vtl, name, t );
908 vik_track_convert (t, vtl->coord_mode);
911 // Consider if redraw necessary for the new item
912 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
913 vik_layer_emit_update ( VIK_LAYER(vtl) );
916 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
920 t = vik_track_unmarshall ( item, len );
921 // When copying - we'll create a new name based on the original
922 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
923 vik_trw_layer_add_route ( vtl, name, t );
924 vik_track_convert (t, vtl->coord_mode);
927 // Consider if redraw necessary for the new item
928 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
929 vik_layer_emit_update ( VIK_LAYER(vtl) );
935 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
942 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
946 case PARAM_TV: vtl->tracks_visible = data.b; break;
947 case PARAM_WV: vtl->waypoints_visible = data.b; break;
948 case PARAM_RV: vtl->routes_visible = data.b; break;
949 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
950 case PARAM_TLFONTSIZE:
951 if ( data.u < FS_NUM_SIZES ) {
952 vtl->track_font_size = data.u;
953 g_free ( vtl->track_fsize_str );
954 switch ( vtl->track_font_size ) {
955 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
956 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
957 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
958 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
959 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
960 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
961 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
965 case PARAM_DM: vtl->drawmode = data.u; break;
967 vtl->track_color = data.c;
968 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
970 case PARAM_DP: vtl->drawpoints = data.b; break;
972 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
973 vtl->drawpoints_size = data.u;
975 case PARAM_DE: vtl->drawelevation = data.b; break;
976 case PARAM_DS: vtl->drawstops = data.b; break;
977 case PARAM_DL: vtl->drawlines = data.b; break;
978 case PARAM_DD: vtl->drawdirections = data.b; break;
980 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
981 vtl->drawdirections_size = data.u;
983 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
984 vtl->stop_length = data.u;
986 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
987 vtl->elevation_factor = data.u;
989 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
991 vtl->line_thickness = data.u;
992 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
995 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
997 vtl->bg_line_thickness = data.u;
998 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1002 vtl->track_bg_color = data.c;
1003 if ( vtl->track_bg_gc )
1004 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1006 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1007 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1008 case PARAM_DLA: vtl->drawlabels = data.b; break;
1009 case PARAM_DI: vtl->drawimages = data.b; break;
1010 case PARAM_IS: if ( data.u != vtl->image_size )
1012 vtl->image_size = data.u;
1013 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1014 g_queue_free ( vtl->image_cache );
1015 vtl->image_cache = g_queue_new ();
1018 case PARAM_IA: vtl->image_alpha = data.u; break;
1019 case PARAM_ICS: vtl->image_cache_size = data.u;
1020 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1021 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1024 vtl->waypoint_color = data.c;
1025 if ( vtl->waypoint_gc )
1026 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1029 vtl->waypoint_text_color = data.c;
1030 if ( vtl->waypoint_text_gc )
1031 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1034 vtl->waypoint_bg_color = data.c;
1035 if ( vtl->waypoint_bg_gc )
1036 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1039 vtl->wpbgand = data.b;
1040 if ( vtl->waypoint_bg_gc )
1041 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1043 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1044 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1045 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1046 case PARAM_WPFONTSIZE:
1047 if ( data.u < FS_NUM_SIZES ) {
1048 vtl->wp_font_size = data.u;
1049 g_free ( vtl->wp_fsize_str );
1050 switch ( vtl->wp_font_size ) {
1051 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1052 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1053 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1054 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1055 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1056 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1057 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1061 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1066 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1068 VikLayerParamData rv;
1071 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1072 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1073 case PARAM_RV: rv.b = vtl->routes_visible; break;
1074 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1075 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1076 case PARAM_DM: rv.u = vtl->drawmode; break;
1077 case PARAM_TC: rv.c = vtl->track_color; break;
1078 case PARAM_DP: rv.b = vtl->drawpoints; break;
1079 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1080 case PARAM_DE: rv.b = vtl->drawelevation; break;
1081 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1082 case PARAM_DS: rv.b = vtl->drawstops; break;
1083 case PARAM_SL: rv.u = vtl->stop_length; break;
1084 case PARAM_DL: rv.b = vtl->drawlines; break;
1085 case PARAM_DD: rv.b = vtl->drawdirections; break;
1086 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1087 case PARAM_LT: rv.u = vtl->line_thickness; break;
1088 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1089 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1090 case PARAM_DI: rv.b = vtl->drawimages; break;
1091 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1092 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1093 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1094 case PARAM_IS: rv.u = vtl->image_size; break;
1095 case PARAM_IA: rv.u = vtl->image_alpha; break;
1096 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1097 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1098 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1099 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1100 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1101 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1102 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1103 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1104 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1105 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1110 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1117 // Use byte arrays to store sublayer data
1118 // much like done elsewhere e.g. vik_layer_marshall_params()
1119 GByteArray *ba = g_byte_array_new ( );
1124 guint object_length;
1127 // the length of the item
1128 // the sublayer type of item
1129 // the the actual item
1130 #define tlm_append(object_pointer, size, type) \
1132 object_length = (size); \
1133 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1134 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1135 g_byte_array_append ( ba, (object_pointer), object_length );
1137 // Layer parameters first
1138 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1139 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1140 g_byte_array_append ( ba, pd, pl );
1143 // Now sublayer data
1144 GHashTableIter iter;
1145 gpointer key, value;
1148 g_hash_table_iter_init ( &iter, vtl->waypoints );
1149 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1150 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1151 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1156 g_hash_table_iter_init ( &iter, vtl->tracks );
1157 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1158 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1159 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1164 g_hash_table_iter_init ( &iter, vtl->routes );
1165 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1166 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1167 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1177 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1179 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1181 gint consumed_length;
1183 // First the overall layer parameters
1184 memcpy(&pl, data, sizeof(pl));
1186 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1189 consumed_length = pl;
1190 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1192 #define tlm_size (*(gint *)data)
1193 // See marshalling above for order of how this is written
1195 data += sizeof_len_and_subtype + tlm_size;
1197 // Now the individual sublayers:
1199 while ( *data && consumed_length < len ) {
1200 // Normally four extra bytes at the end of the datastream
1201 // (since it's a GByteArray and that's where it's length is stored)
1202 // So only attempt read when there's an actual block of sublayer data
1203 if ( consumed_length + tlm_size < len ) {
1205 // Reuse pl to read the subtype from the data stream
1206 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1208 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1209 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1210 gchar *name = g_strdup ( trk->name );
1211 vik_trw_layer_add_track ( vtl, name, trk );
1214 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1215 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1216 gchar *name = g_strdup ( wp->name );
1217 vik_trw_layer_add_waypoint ( vtl, name, wp );
1220 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1221 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1222 gchar *name = g_strdup ( trk->name );
1223 vik_trw_layer_add_route ( vtl, name, trk );
1227 consumed_length += tlm_size + sizeof_len_and_subtype;
1230 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1232 // Not stored anywhere else so need to regenerate
1233 trw_layer_calculate_bounds_waypoints ( vtl );
1238 // Keep interesting hash function at least visible
1240 static guint strcase_hash(gconstpointer v)
1242 // 31 bit hash function
1245 gchar s[128]; // malloc is too slow for reading big files
1248 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1249 p[i] = toupper(t[i]);
1255 for (p += 1; *p != '\0'; p++)
1256 h = (h << 5) - h + *p;
1263 // Stick a 1 at the end of the function name to make it more unique
1264 // thus more easily searchable in a simple text editor
1265 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1267 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1268 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1270 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1271 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1273 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1274 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1275 // and with normal PC processing capabilities - it has negligibile performance impact
1276 // This also minimized the amount of rework - as the management of the hash tables already exists.
1278 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1279 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1280 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1282 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1283 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1284 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1285 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1286 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1287 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1289 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1291 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1293 // Param settings that are not available via the GUI
1294 // Force to on after processing params (which defaults them to off with a zero value)
1295 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1297 rv->draw_sync_done = TRUE;
1298 rv->draw_sync_do = TRUE;
1299 // Everything else is 0, FALSE or NULL
1305 static void trw_layer_free ( VikTrwLayer *trwlayer )
1307 g_hash_table_destroy(trwlayer->waypoints);
1308 g_hash_table_destroy(trwlayer->waypoints_iters);
1309 g_hash_table_destroy(trwlayer->tracks);
1310 g_hash_table_destroy(trwlayer->tracks_iters);
1311 g_hash_table_destroy(trwlayer->routes);
1312 g_hash_table_destroy(trwlayer->routes_iters);
1314 /* ODC: replace with GArray */
1315 trw_layer_free_track_gcs ( trwlayer );
1317 if ( trwlayer->wp_right_click_menu )
1318 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1320 if ( trwlayer->track_right_click_menu )
1321 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1323 if ( trwlayer->tracklabellayout != NULL)
1324 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1326 if ( trwlayer->wplabellayout != NULL)
1327 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1329 if ( trwlayer->waypoint_gc != NULL )
1330 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1332 if ( trwlayer->waypoint_text_gc != NULL )
1333 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1335 if ( trwlayer->waypoint_bg_gc != NULL )
1336 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1338 g_free ( trwlayer->wp_fsize_str );
1339 g_free ( trwlayer->track_fsize_str );
1341 if ( trwlayer->tpwin != NULL )
1342 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1344 if ( trwlayer->tracks_analysis_dialog != NULL )
1345 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1347 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1348 g_queue_free ( trwlayer->image_cache );
1351 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1355 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1356 dp->xmpp = vik_viewport_get_xmpp ( vp );
1357 dp->ympp = vik_viewport_get_ympp ( vp );
1358 dp->width = vik_viewport_get_width ( vp );
1359 dp->height = vik_viewport_get_height ( vp );
1360 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1361 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1363 dp->center = vik_viewport_get_center ( vp );
1364 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1365 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1370 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1371 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1372 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1374 dp->ce1 = dp->center->east_west-w2;
1375 dp->ce2 = dp->center->east_west+w2;
1376 dp->cn1 = dp->center->north_south-h2;
1377 dp->cn2 = dp->center->north_south+h2;
1378 } else if ( dp->lat_lon ) {
1379 VikCoord upperleft, bottomright;
1380 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1381 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1382 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1383 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1384 dp->ce1 = upperleft.east_west;
1385 dp->ce2 = bottomright.east_west;
1386 dp->cn1 = bottomright.north_south;
1387 dp->cn2 = upperleft.north_south;
1390 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1394 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1395 * Here a simple traffic like light colour system is used:
1396 * . slow points are red
1397 * . average is yellow
1398 * . fast points are green
1400 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1403 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1404 if ( average_speed > 0 ) {
1405 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1406 if ( rv < low_speed )
1407 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1408 else if ( rv > high_speed )
1409 return VIK_TRW_LAYER_TRACK_GC_FAST;
1411 return VIK_TRW_LAYER_TRACK_GC_AVER;
1414 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1417 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1419 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1420 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1421 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1422 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1426 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1428 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1430 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1431 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1433 // Fallback if parse failure
1434 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1436 g_free ( label_markup );
1438 gint label_x, label_y;
1440 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1442 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1443 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1447 * distance_in_preferred_units:
1448 * @dist: The source distance in standard SI Units (i.e. metres)
1450 * TODO: This is a generic function that could be moved into globals.c or utils.c
1452 * Probably best used if you have a only few conversions to perform.
1453 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1454 * since it will be doing the preference check on each call
1456 * Returns: The distance in the units as specified by the preferences
1458 static gdouble distance_in_preferred_units ( gdouble dist )
1461 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1462 switch (dist_units) {
1463 case VIK_UNITS_DISTANCE_MILES:
1464 mydist = VIK_METERS_TO_MILES(dist);
1466 // VIK_UNITS_DISTANCE_KILOMETRES:
1468 mydist = dist/1000.0;
1475 * trw_layer_draw_dist_labels:
1477 * Draw a few labels along a track at nicely seperated distances
1478 * This might slow things down if there's many tracks being displayed with this on.
1480 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1482 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1483 25.0, 40.0, 50.0, 75.0, 100.0,
1484 150.0, 200.0, 250.0, 500.0, 1000.0};
1486 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1488 // Convert to specified unit to find the friendly breakdown value
1489 dist = distance_in_preferred_units ( dist );
1493 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1494 if ( chunksd[i] > dist ) {
1496 dist = chunksd[index];
1501 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1503 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1504 gdouble dist_i = dist * i;
1506 // Convert distance back into metres for use in finding a trackpoint
1507 switch (dist_units) {
1508 case VIK_UNITS_DISTANCE_MILES:
1509 dist_i = VIK_MILES_TO_METERS(dist_i);
1511 // VIK_UNITS_DISTANCE_KILOMETRES:
1513 dist_i = dist_i*1000.0;
1517 gdouble dist_current = 0.0;
1518 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1519 gdouble dist_next = 0.0;
1520 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1522 gdouble dist_between_tps = fabs (dist_next - dist_current);
1523 gdouble ratio = 0.0;
1524 // Prevent division by 0 errors
1525 if ( dist_between_tps > 0.0 )
1526 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1528 if ( tp_current && tp_next ) {
1529 // Construct the name based on the distance value
1532 switch (dist_units) {
1533 case VIK_UNITS_DISTANCE_MILES:
1534 units = g_strdup ( _("miles") );
1536 // VIK_UNITS_DISTANCE_KILOMETRES:
1538 units = g_strdup ( _("km") );
1542 // Convert for display
1543 dist_i = distance_in_preferred_units ( dist_i );
1545 // Make the precision of the output related to the unit size.
1547 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1548 else if ( index == 1 )
1549 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1551 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1554 struct LatLon ll_current, ll_next;
1555 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1556 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1558 // positional interpolation
1559 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1560 // but should be good enough over the small scale that I anticipate usage on
1561 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1562 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1564 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1567 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1568 fgcolour = gdk_color_to_string ( &(trk->color) );
1570 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1572 // if highlight mode on, then colour the background in the highlight colour
1574 if ( drawing_highlight )
1575 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1577 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1579 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1581 g_free ( fgcolour );
1582 g_free ( bgcolour );
1589 * trw_layer_draw_track_name_labels:
1591 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1593 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1596 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1597 fgcolour = gdk_color_to_string ( &(trk->color) );
1599 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1601 // if highlight mode on, then colour the background in the highlight colour
1603 if ( drawing_highlight )
1604 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1606 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1608 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1610 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1611 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1612 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1613 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1614 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1615 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1617 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1619 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1622 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1623 // No other labels to draw
1626 GList *ending = g_list_last ( trk->trackpoints );
1627 VikCoord begin_coord = VIK_TRACKPOINT(trk->trackpoints->data)->coord;
1628 VikCoord end_coord = VIK_TRACKPOINT(ending->data)->coord;
1630 gboolean done_start_end = FALSE;
1632 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1633 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1635 // This number can be configured via the settings if you really want to change it
1636 gdouble distance_diff;
1637 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1638 distance_diff = 100.0; // Metres
1640 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1641 // Start and end 'close' together so only draw one label at an average location
1642 gint x1, x2, y1, y2;
1643 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1644 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1646 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1648 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1649 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1652 done_start_end = TRUE;
1656 if ( ! done_start_end ) {
1657 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1658 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1659 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1660 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1661 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1662 g_free ( name_start );
1664 // Don't draw end label if this is the one being created
1665 if ( trk != dp->vtl->current_track ) {
1666 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1667 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1668 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1669 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1670 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1671 g_free ( name_end );
1676 g_free ( fgcolour );
1677 g_free ( bgcolour );
1681 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1683 if ( ! track->visible )
1686 /* TODO: this function is a mess, get rid of any redundancy */
1687 GList *list = track->trackpoints;
1689 gboolean useoldvals = TRUE;
1691 gboolean drawpoints;
1693 gboolean drawelevation;
1694 gdouble min_alt, max_alt, alt_diff = 0;
1696 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1697 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1700 if ( dp->vtl->drawelevation )
1702 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1703 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1704 alt_diff = max_alt - min_alt;
1707 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1708 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1709 trw_layer_draw_track ( id, track, dp, TRUE );
1711 if ( draw_track_outline )
1712 drawpoints = drawstops = FALSE;
1714 drawpoints = dp->vtl->drawpoints;
1715 drawstops = dp->vtl->drawstops;
1718 gboolean drawing_highlight = FALSE;
1719 /* Current track - used for creation */
1720 if ( track == dp->vtl->current_track )
1721 main_gc = dp->vtl->current_track_gc;
1723 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1724 /* Draw all tracks of the layer in special colour */
1725 /* if track is member of selected layer or is the current selected track
1726 then draw in the highlight colour.
1727 NB this supercedes the drawmode */
1728 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1729 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1730 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1731 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1732 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1733 drawing_highlight = TRUE;
1736 if ( !drawing_highlight ) {
1737 // Still need to figure out the gc according to the drawing mode:
1738 switch ( dp->vtl->drawmode ) {
1739 case DRAWMODE_BY_TRACK:
1740 if ( dp->vtl->track_1color_gc )
1741 g_object_unref ( dp->vtl->track_1color_gc );
1742 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1743 main_gc = dp->vtl->track_1color_gc;
1746 // Mostly for DRAWMODE_ALL_SAME_COLOR
1747 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1748 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1755 int x, y, oldx, oldy;
1756 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1758 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1760 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1762 // Draw the first point as something a bit different from the normal points
1763 // ATM it's slightly bigger and a triangle
1765 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1766 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1772 gdouble average_speed = 0.0;
1773 gdouble low_speed = 0.0;
1774 gdouble high_speed = 0.0;
1775 // If necessary calculate these values - which is done only once per track redraw
1776 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1777 // the percentage factor away from the average speed determines transistions between the levels
1778 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1779 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1780 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1783 while ((list = g_list_next(list)))
1785 tp = VIK_TRACKPOINT(list->data);
1786 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1788 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1789 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1790 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1791 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1792 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1794 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1797 * If points are the same in display coordinates, don't draw.
1799 if ( useoldvals && x == oldx && y == oldy )
1801 // Still need to process points to ensure 'stops' are drawn if required
1802 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1803 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1804 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 );
1809 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1810 if ( drawpoints || dp->vtl->drawlines ) {
1811 // setup main_gc for both point and line drawing
1812 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1813 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 ) );
1817 if ( drawpoints && ! draw_track_outline )
1822 * The concept of drawing stops is that a trackpoint
1823 * that is if the next trackpoint has a timestamp far into
1824 * the future, we draw a circle of 6x trackpoint size,
1825 * instead of a rectangle of 2x trackpoint size.
1826 * This is drawn first so the trackpoint will be drawn on top
1829 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1830 /* Stop point. Draw 6x circle. Always in redish colour */
1831 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 );
1833 /* Regular point - draw 2x square. */
1834 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1837 /* Final point - draw 4x circle. */
1838 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 );
1841 if ((!tp->newsegment) && (dp->vtl->drawlines))
1844 /* UTM only: zone check */
1845 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1846 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1849 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1851 if ( draw_track_outline ) {
1852 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1856 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1858 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1860 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1865 tmp[1].y = oldy-FIXALTITUDE(list->data);
1867 tmp[2].y = y-FIXALTITUDE(list->next->data);
1872 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1873 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1875 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1876 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1878 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1883 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1884 // Draw an arrow at the mid point to show the direction of the track
1885 // Code is a rework from vikwindow::draw_ruler()
1886 gint midx = (oldx + x) / 2;
1887 gint midy = (oldy + y) / 2;
1889 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1890 // Avoid divide by zero and ensure at least 1 pixel big
1892 gdouble dx = (oldx - midx) / len;
1893 gdouble dy = (oldy - midy) / len;
1894 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1895 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1905 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1907 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1908 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1910 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1912 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1913 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 ));
1917 * If points are the same in display coordinates, don't draw.
1919 if ( x != oldx || y != oldy )
1921 if ( draw_track_outline )
1922 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1924 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1930 * If points are the same in display coordinates, don't draw.
1932 if ( x != oldx && y != oldy )
1934 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1935 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1943 // Labels drawn after the trackpoints, so the labels are on top
1944 if ( dp->vtl->track_draw_labels ) {
1945 if ( track->max_number_dist_labels > 0 ) {
1946 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
1949 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
1950 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
1956 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1958 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1959 trw_layer_draw_track ( id, track, dp, FALSE );
1963 static void cached_pixbuf_free ( CachedPixbuf *cp )
1965 g_object_unref ( G_OBJECT(cp->pixbuf) );
1966 g_free ( cp->image );
1969 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1971 return strcmp ( cp->image, name );
1974 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1977 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1978 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1979 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1982 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1984 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1986 if ( wp->image && dp->vtl->drawimages )
1988 GdkPixbuf *pixbuf = NULL;
1991 if ( dp->vtl->image_alpha == 0)
1994 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1996 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1999 gchar *image = wp->image;
2000 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2001 if ( ! regularthumb )
2003 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2004 image = "\x12\x00"; /* this shouldn't occur naturally. */
2008 CachedPixbuf *cp = NULL;
2009 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2010 if ( dp->vtl->image_size == 128 )
2011 cp->pixbuf = regularthumb;
2014 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2015 g_assert ( cp->pixbuf );
2016 g_object_unref ( G_OBJECT(regularthumb) );
2018 cp->image = g_strdup ( image );
2020 /* needed so 'click picture' tool knows how big the pic is; we don't
2021 * store it in cp because they may have been freed already. */
2022 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2023 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2025 g_queue_push_head ( dp->vtl->image_cache, cp );
2026 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2027 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2029 pixbuf = cp->pixbuf;
2033 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2039 w = gdk_pixbuf_get_width ( pixbuf );
2040 h = gdk_pixbuf_get_height ( pixbuf );
2042 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2044 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2045 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2046 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2047 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2048 // Highlighted - so draw a little border around the chosen one
2049 // single line seems a little weak so draw 2 of them
2050 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2051 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2052 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2053 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2056 if ( dp->vtl->image_alpha == 255 )
2057 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2059 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2061 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2065 // Draw appropriate symbol - either symbol image or simple types
2066 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2067 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 );
2069 else if ( wp == dp->vtl->current_wp ) {
2070 switch ( dp->vtl->wp_symbol ) {
2071 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;
2072 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;
2073 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;
2074 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 );
2075 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 );
2079 switch ( dp->vtl->wp_symbol ) {
2080 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;
2081 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;
2082 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;
2083 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 );
2084 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;
2088 if ( dp->vtl->drawlabels )
2090 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2091 gint label_x, label_y;
2093 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2095 // Could this stored in the waypoint rather than recreating each pass?
2096 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2098 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2099 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2101 // Fallback if parse failure
2102 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2104 g_free ( wp_label_markup );
2106 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2107 label_x = x - width/2;
2108 if ( wp->symbol_pixbuf )
2109 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2111 label_y = y - dp->vtl->wp_size - height - 2;
2113 /* if highlight mode on, then draw background text in highlight colour */
2114 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2115 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2116 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2117 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2118 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2120 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2123 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2125 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2130 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2132 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2133 trw_layer_draw_waypoint ( id, wp, dp );
2137 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2139 static struct DrawingParams dp;
2140 g_assert ( l != NULL );
2142 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2144 if ( l->tracks_visible )
2145 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2147 if ( l->routes_visible )
2148 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2150 if (l->waypoints_visible)
2151 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2154 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2157 if ( vtl->track_bg_gc )
2159 g_object_unref ( vtl->track_bg_gc );
2160 vtl->track_bg_gc = NULL;
2162 if ( vtl->track_1color_gc )
2164 g_object_unref ( vtl->track_1color_gc );
2165 vtl->track_1color_gc = NULL;
2167 if ( vtl->current_track_gc )
2169 g_object_unref ( vtl->current_track_gc );
2170 vtl->current_track_gc = NULL;
2172 if ( vtl->current_track_newpoint_gc )
2174 g_object_unref ( vtl->current_track_newpoint_gc );
2175 vtl->current_track_newpoint_gc = NULL;
2178 if ( ! vtl->track_gc )
2180 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2181 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2182 g_array_free ( vtl->track_gc, TRUE );
2183 vtl->track_gc = NULL;
2186 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2188 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2189 gint width = vtl->line_thickness;
2191 if ( vtl->track_gc )
2192 trw_layer_free_track_gcs ( vtl );
2194 if ( vtl->track_bg_gc )
2195 g_object_unref ( vtl->track_bg_gc );
2196 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2198 // Ensure new track drawing heeds line thickness setting
2199 // however always have a minium of 2, as 1 pixel is really narrow
2200 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2202 if ( vtl->current_track_gc )
2203 g_object_unref ( vtl->current_track_gc );
2204 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2205 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2207 // 'newpoint' gc is exactly the same as the current track gc
2208 if ( vtl->current_track_newpoint_gc )
2209 g_object_unref ( vtl->current_track_newpoint_gc );
2210 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2211 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2213 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2215 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2216 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2218 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2219 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2220 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2222 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2224 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2227 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2229 VikTrwLayer *rv = trw_layer_new1 ( vp );
2230 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2232 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2233 /* early exit, as the rest is GUI related */
2237 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2238 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2240 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2241 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2243 trw_layer_new_track_gcs ( rv, vp );
2245 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2246 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2247 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2248 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2250 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2252 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2257 #define SMALL_ICON_SIZE 18
2259 * Can accept a null symbol, and may return null value
2261 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2263 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2264 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2265 // So needing a small icon for the treeview may need some resizing:
2266 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2267 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2271 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2273 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2275 GdkPixbuf *pixbuf = NULL;
2277 if ( track->has_color ) {
2278 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2279 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2280 // Here is some magic found to do the conversion
2281 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2282 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2283 ((track->color.green & 0xff00) << 8) |
2284 (track->color.blue & 0xff00);
2286 gdk_pixbuf_fill ( pixbuf, pixel );
2289 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 );
2292 g_object_unref (pixbuf);
2294 *new_iter = *((GtkTreeIter *) pass_along[1]);
2295 if ( track->is_route )
2296 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2298 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2300 if ( ! track->visible )
2301 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2304 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2306 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2308 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 );
2310 *new_iter = *((GtkTreeIter *) pass_along[1]);
2311 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2313 if ( ! wp->visible )
2314 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2317 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2319 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2322 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2324 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2327 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2329 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2332 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2335 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2337 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2338 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2340 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2342 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2345 if ( g_hash_table_size (vtl->routes) > 0 ) {
2346 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2348 pass_along[0] = &(vtl->routes_iter);
2349 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2351 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2353 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2356 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2357 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2359 pass_along[0] = &(vtl->waypoints_iter);
2360 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2362 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2364 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2369 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2373 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2374 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2375 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2376 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2378 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2380 return (t->visible ^= 1);
2384 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2386 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2388 return (t->visible ^= 1);
2392 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2394 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2396 return (t->visible ^= 1);
2405 * Return a property about tracks for this layer
2407 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2409 return vtl->line_thickness;
2412 // Structure to hold multiple track information for a layer
2421 * Build up layer multiple track information via updating the tooltip_tracks structure
2423 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2425 tt->length = tt->length + vik_track_get_length (tr);
2427 // Ensure times are available
2428 if ( tr->trackpoints &&
2429 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2430 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2433 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2434 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2436 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2437 // Hence initialize to the first 'proper' value
2438 if ( tt->start_time == 0 )
2439 tt->start_time = t1;
2440 if ( tt->end_time == 0 )
2443 // Update find the earliest / last times
2444 if ( t1 < tt->start_time )
2445 tt->start_time = t1;
2446 if ( t2 > tt->end_time )
2449 // Keep track of total time
2450 // there maybe gaps within a track (eg segments)
2451 // but this should be generally good enough for a simple indicator
2452 tt->duration = tt->duration + (int)(t2-t1);
2457 * Generate tooltip text for the layer.
2458 * This is relatively complicated as it considers information for
2459 * no tracks, a single track or multiple tracks
2460 * (which may or may not have timing information)
2462 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2473 static gchar tmp_buf[128];
2476 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2478 // Safety check - I think these should always be valid
2479 if ( vtl->tracks && vtl->waypoints ) {
2480 tooltip_tracks tt = { 0.0, 0, 0 };
2481 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2483 GDate* gdate_start = g_date_new ();
2484 g_date_set_time_t (gdate_start, tt.start_time);
2486 GDate* gdate_end = g_date_new ();
2487 g_date_set_time_t (gdate_end, tt.end_time);
2489 if ( g_date_compare (gdate_start, gdate_end) ) {
2490 // Dates differ so print range on separate line
2491 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2492 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2493 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2496 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2497 if ( tt.start_time != 0 )
2498 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2502 if ( tt.length > 0.0 ) {
2503 gdouble len_in_units;
2505 // Setup info dependent on distance units
2506 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2507 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2508 len_in_units = VIK_METERS_TO_MILES(tt.length);
2511 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2512 len_in_units = tt.length/1000.0;
2515 // Timing information if available
2517 if ( tt.duration > 0 ) {
2518 g_snprintf (tbuf1, sizeof(tbuf1),
2519 _(" in %d:%02d hrs:mins"),
2520 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2522 g_snprintf (tbuf2, sizeof(tbuf2),
2523 _("\n%sTotal Length %.1f %s%s"),
2524 tbuf3, len_in_units, tbuf4, tbuf1);
2527 // Put together all the elements to form compact tooltip text
2528 g_snprintf (tmp_buf, sizeof(tmp_buf),
2529 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2530 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2532 g_date_free (gdate_start);
2533 g_date_free (gdate_end);
2540 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2544 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2546 // Very simple tooltip - may expand detail in the future...
2547 static gchar tmp_buf[32];
2548 g_snprintf (tmp_buf, sizeof(tmp_buf),
2550 g_hash_table_size (l->tracks));
2554 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2556 // Very simple tooltip - may expand detail in the future...
2557 static gchar tmp_buf[32];
2558 g_snprintf (tmp_buf, sizeof(tmp_buf),
2560 g_hash_table_size (l->routes));
2565 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2566 // Same tooltip for a route
2567 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2570 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2571 tr = g_hash_table_lookup ( l->tracks, sublayer );
2573 tr = g_hash_table_lookup ( l->routes, sublayer );
2576 // Could be a better way of handling strings - but this works...
2577 gchar time_buf1[20];
2578 gchar time_buf2[20];
2579 time_buf1[0] = '\0';
2580 time_buf2[0] = '\0';
2581 static gchar tmp_buf[100];
2582 // Compact info: Short date eg (11/20/99), duration and length
2583 // Hopefully these are the things that are most useful and so promoted into the tooltip
2584 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2585 // %x The preferred date representation for the current locale without the time.
2586 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2587 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2588 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2590 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2593 // Get length and consider the appropriate distance units
2594 gdouble tr_len = vik_track_get_length(tr);
2595 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2596 switch (dist_units) {
2597 case VIK_UNITS_DISTANCE_KILOMETRES:
2598 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2600 case VIK_UNITS_DISTANCE_MILES:
2601 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2610 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2612 // Very simple tooltip - may expand detail in the future...
2613 static gchar tmp_buf[32];
2614 g_snprintf (tmp_buf, sizeof(tmp_buf),
2616 g_hash_table_size (l->waypoints));
2620 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2622 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2623 // NB It's OK to return NULL
2628 return w->description;
2637 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2640 * set_statusbar_msg_info_trkpt:
2642 * Function to show track point information on the statusbar
2643 * Items displayed is controlled by the settings format code
2645 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2647 gchar *statusbar_format_code = NULL;
2648 gboolean need2free = FALSE;
2649 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2650 // Otherwise use default
2651 statusbar_format_code = g_strdup ( "KEATDN" );
2655 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2656 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2660 g_free ( statusbar_format_code );
2664 * Function to show basic waypoint information on the statusbar
2666 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2669 switch (a_vik_get_units_height ()) {
2670 case VIK_UNITS_HEIGHT_FEET:
2671 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2674 //VIK_UNITS_HEIGHT_METRES:
2675 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2679 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2680 // one can easily use the current pointer position to see this if needed
2681 gchar *lat = NULL, *lon = NULL;
2682 static struct LatLon ll;
2683 vik_coord_to_latlon (&(wpt->coord), &ll);
2684 a_coords_latlon_to_string ( &ll, &lat, &lon );
2686 // Combine parts to make overall message
2689 // Add comment if available
2690 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2692 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2693 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2700 * General layer selection function, find out which bit is selected and take appropriate action
2702 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2705 l->current_wp = NULL;
2706 l->current_wp_id = NULL;
2707 trw_layer_cancel_current_tp ( l, FALSE );
2710 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2714 case VIK_TREEVIEW_TYPE_LAYER:
2716 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2717 /* Mark for redraw */
2722 case VIK_TREEVIEW_TYPE_SUBLAYER:
2726 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2728 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2729 /* Mark for redraw */
2733 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2735 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2736 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2737 /* Mark for redraw */
2741 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2743 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2744 /* Mark for redraw */
2748 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2750 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2751 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2752 /* Mark for redraw */
2756 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2758 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2759 /* Mark for redraw */
2763 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2765 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2767 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2768 // Show some waypoint info
2769 set_statusbar_msg_info_wpt ( l, wpt );
2770 /* Mark for redraw */
2777 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2786 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2791 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2796 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2801 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2803 return l->waypoints;
2806 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
2808 return vtl->tracks_iters;
2811 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
2813 return vtl->routes_iters;
2816 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
2818 return vtl->waypoints;
2821 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2823 return ! ( g_hash_table_size ( vtl->tracks ) ||
2824 g_hash_table_size ( vtl->routes ) ||
2825 g_hash_table_size ( vtl->waypoints ) );
2828 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2830 return vtl->tracks_visible;
2833 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2835 return vtl->routes_visible;
2838 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2840 return vtl->waypoints_visible;
2844 * ATM use a case sensitive find
2845 * Finds the first one
2847 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2849 if ( wp && wp->name )
2850 if ( ! strcmp ( wp->name, name ) )
2856 * Get waypoint by name - not guaranteed to be unique
2857 * Finds the first one
2859 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2861 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2865 * ATM use a case sensitive find
2866 * Finds the first one
2868 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2870 if ( trk && trk->name )
2871 if ( ! strcmp ( trk->name, name ) )
2877 * Get track by name - not guaranteed to be unique
2878 * Finds the first one
2880 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2882 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2886 * Get route by name - not guaranteed to be unique
2887 * Finds the first one
2889 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2891 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2894 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2896 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2897 maxmin[0].lat = trk->bbox.north;
2898 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2899 maxmin[1].lat = trk->bbox.south;
2900 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2901 maxmin[0].lon = trk->bbox.east;
2902 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2903 maxmin[1].lon = trk->bbox.west;
2906 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2908 // Continually reuse maxmin to find the latest maximum and minimum values
2909 // First set to waypoints bounds
2910 maxmin[0].lat = vtl->waypoints_bbox.north;
2911 maxmin[1].lat = vtl->waypoints_bbox.south;
2912 maxmin[0].lon = vtl->waypoints_bbox.east;
2913 maxmin[1].lon = vtl->waypoints_bbox.west;
2914 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2915 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2918 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2920 /* 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... */
2921 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2922 trw_layer_find_maxmin (vtl, maxmin);
2923 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2927 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2928 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2933 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2936 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2937 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2939 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2942 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2944 /* First set the center [in case previously viewing from elsewhere] */
2945 /* Then loop through zoom levels until provided positions are in view */
2946 /* This method is not particularly fast - but should work well enough */
2947 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2949 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2950 vik_viewport_set_center_coord ( vvp, &coord );
2952 /* Convert into definite 'smallest' and 'largest' positions */
2953 struct LatLon minmin;
2954 if ( maxmin[0].lat < maxmin[1].lat )
2955 minmin.lat = maxmin[0].lat;
2957 minmin.lat = maxmin[1].lat;
2959 struct LatLon maxmax;
2960 if ( maxmin[0].lon > maxmin[1].lon )
2961 maxmax.lon = maxmin[0].lon;
2963 maxmax.lon = maxmin[1].lon;
2965 /* Never zoom in too far - generally not that useful, as too close ! */
2966 /* Always recalculate the 'best' zoom level */
2968 vik_viewport_set_zoom ( vvp, zoom );
2970 gdouble min_lat, max_lat, min_lon, max_lon;
2971 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2972 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2973 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2974 /* NB I think the logic used in this test to determine if the bounds is within view
2975 fails if track goes across 180 degrees longitude.
2976 Hopefully that situation is not too common...
2977 Mind you viking doesn't really do edge locations to well anyway */
2978 if ( min_lat < minmin.lat &&
2979 max_lat > minmin.lat &&
2980 min_lon < maxmax.lon &&
2981 max_lon > maxmax.lon )
2982 /* Found within zoom level */
2987 vik_viewport_set_zoom ( vvp, zoom );
2991 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2993 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2994 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2995 trw_layer_find_maxmin (vtl, maxmin);
2996 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2999 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3004 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
3006 if ( vik_trw_layer_auto_set_view ( VIK_TRW_LAYER(layer_and_vlp[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(layer_and_vlp[1])) ) ) {
3007 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3010 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
3013 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3015 GtkWidget *file_selector;
3017 gboolean failed = FALSE;
3018 file_selector = gtk_file_chooser_dialog_new (title,
3020 GTK_FILE_CHOOSER_ACTION_SAVE,
3021 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3022 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3024 gchar *cwd = g_get_current_dir();
3026 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3030 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3032 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3034 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3035 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
3037 gtk_widget_hide ( file_selector );
3038 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3039 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3040 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3045 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3047 gtk_widget_hide ( file_selector );
3048 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3049 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3050 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3055 gtk_widget_destroy ( file_selector );
3057 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
3060 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
3062 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
3065 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
3067 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
3070 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
3072 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3073 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3074 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3075 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3077 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3079 g_free ( auto_save_name );
3082 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
3084 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3085 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3086 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3087 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3089 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3091 g_free ( auto_save_name );
3095 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3098 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
3100 gchar *name_used = NULL;
3103 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3104 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3105 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
3106 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3108 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
3112 gchar *quoted_file = g_shell_quote ( name_used );
3113 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3114 g_free ( quoted_file );
3115 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3117 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
3118 g_error_free ( err );
3122 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3123 //g_remove ( name_used );
3124 // Perhaps should be deleted when the program ends?
3125 // For now leave it to the user to delete it / use system temp cleanup methods.
3126 g_free ( name_used );
3130 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
3132 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
3135 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
3137 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
3140 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
3142 gpointer layer_and_vlp[2];
3143 layer_and_vlp[0] = pass_along[0];
3144 layer_and_vlp[1] = pass_along[1];
3146 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3148 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3149 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3151 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3153 if ( !trk || !trk->name )
3156 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3157 gchar *auto_save_name = g_strdup ( trk->name );
3158 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3159 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3161 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
3163 g_free ( auto_save_name );
3166 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3168 wpu_udata *user_data = udata;
3169 if ( wp == user_data->wp ) {
3170 user_data->uuid = id;
3176 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
3178 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3179 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
3180 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3182 GTK_RESPONSE_REJECT,
3184 GTK_RESPONSE_ACCEPT,
3187 GtkWidget *label, *entry;
3188 label = gtk_label_new(_("Waypoint Name:"));
3189 entry = gtk_entry_new();
3191 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3192 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3193 gtk_widget_show_all ( label );
3194 gtk_widget_show_all ( entry );
3196 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3198 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3200 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3201 // Find *first* wp with the given name
3202 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
3205 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
3208 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
3209 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3211 // Find and select on the side panel
3216 // Hmmm, want key of it
3217 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3219 if ( wpf && udata.uuid ) {
3220 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
3221 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
3230 gtk_widget_destroy ( dia );
3233 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3235 gchar *default_name = highest_wp_number_get(vtl);
3236 VikWaypoint *wp = vik_waypoint_new();
3237 gchar *returned_name;
3239 wp->coord = *def_coord;
3241 // Attempt to auto set height if DEM data is available
3242 vik_waypoint_apply_dem_data ( wp, TRUE );
3244 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3246 if ( returned_name )
3249 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3250 g_free (default_name);
3251 g_free (returned_name);
3254 g_free (default_name);
3255 vik_waypoint_free(wp);
3259 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
3261 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3262 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3263 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3264 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3265 VikViewport *vvp = vik_window_viewport(vw);
3267 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3268 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3269 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3270 trw_layer_calculate_bounds_waypoints ( vtl );
3271 vik_layers_panel_emit_update ( vlp );
3274 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
3276 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3277 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3278 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3280 trw_layer_find_maxmin (vtl, maxmin);
3281 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3282 trw_layer_calculate_bounds_waypoints ( vtl );
3283 vik_layers_panel_emit_update ( vlp );
3286 #ifdef VIK_CONFIG_GEOTAG
3287 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
3289 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3291 // Update directly - not changing the mtime
3292 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3295 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3297 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3300 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3304 * Use code in separate file for this feature as reasonably complex
3306 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3308 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3309 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3310 // Unset so can be reverified later if necessary
3311 vtl->has_verified_thumbnails = FALSE;
3313 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3319 static void trw_layer_geotagging ( gpointer lav[2] )
3321 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3322 // Unset so can be reverified later if necessary
3323 vtl->has_verified_thumbnails = FALSE;
3325 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3332 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3335 * Acquire into this TRW Layer straight from GPS Device
3337 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3339 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3340 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3341 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3342 VikViewport *vvp = vik_window_viewport(vw);
3344 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3345 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3349 * Acquire into this TRW Layer from Directions
3351 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3353 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3354 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3355 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3356 VikViewport *vvp = vik_window_viewport(vw);
3358 a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3361 #ifdef VIK_CONFIG_OPENSTREETMAP
3363 * Acquire into this TRW Layer from OSM
3365 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3367 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3368 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3369 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3370 VikViewport *vvp = vik_window_viewport(vw);
3372 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3376 * Acquire into this TRW Layer from OSM for 'My' Traces
3378 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3380 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3381 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3382 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3383 VikViewport *vvp = vik_window_viewport(vw);
3385 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3389 #ifdef VIK_CONFIG_GEOCACHES
3391 * Acquire into this TRW Layer from Geocaching.com
3393 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3395 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3396 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3397 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3398 VikViewport *vvp = vik_window_viewport(vw);
3400 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3404 #ifdef VIK_CONFIG_GEOTAG
3406 * Acquire into this TRW Layer from images
3408 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3410 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3411 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3412 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3413 VikViewport *vvp = vik_window_viewport(vw);
3415 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3416 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3418 // Reverify thumbnails as they may have changed
3419 vtl->has_verified_thumbnails = FALSE;
3420 trw_layer_verify_thumbnails ( vtl, NULL );
3424 static void trw_layer_gps_upload ( gpointer lav[2] )
3426 gpointer pass_along[6];
3427 pass_along[0] = lav[0];
3428 pass_along[1] = lav[1];
3429 pass_along[2] = NULL; // No track - operate on the layer
3430 pass_along[3] = NULL;
3431 pass_along[4] = NULL;
3432 pass_along[5] = NULL;
3434 trw_layer_gps_upload_any ( pass_along );
3438 * If pass_along[3] is defined that this will upload just that track
3440 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3442 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3443 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3445 // May not actually get a track here as pass_along[2&3] can be null
3446 VikTrack *track = NULL;
3447 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3448 gboolean xfer_all = FALSE;
3450 if ( pass_along[2] ) {
3452 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3453 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3456 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3457 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3460 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3463 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3467 else if ( !pass_along[4] )
3468 xfer_all = TRUE; // i.e. whole layer
3470 if (track && !track->visible) {
3471 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3475 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3476 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3477 GTK_DIALOG_DESTROY_WITH_PARENT,
3479 GTK_RESPONSE_ACCEPT,
3481 GTK_RESPONSE_REJECT,
3484 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3485 GtkWidget *response_w = NULL;
3486 #if GTK_CHECK_VERSION (2, 20, 0)
3487 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3491 gtk_widget_grab_focus ( response_w );
3493 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3495 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3496 datasource_gps_clean_up ( dgs );
3497 gtk_widget_destroy ( dialog );
3501 // Get info from reused datasource dialog widgets
3502 gchar* protocol = datasource_gps_get_protocol ( dgs );
3503 gchar* port = datasource_gps_get_descriptor ( dgs );
3504 // NB don't free the above strings as they're references to values held elsewhere
3505 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3506 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3507 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3508 gboolean turn_off = datasource_gps_get_off ( dgs );
3510 gtk_widget_destroy ( dialog );
3512 // When called from the viewport - work the corresponding layerspanel:
3514 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3517 // Apply settings to transfer to the GPS device
3524 vik_layers_panel_get_viewport (vlp),
3533 * Acquire into this TRW Layer from any GPS Babel supported file
3535 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3537 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3538 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3539 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3540 VikViewport *vvp = vik_window_viewport(vw);
3542 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3545 static void trw_layer_new_wp ( gpointer lav[2] )
3547 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3548 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3549 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3550 instead return true if you want to update. */
3551 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 ) {
3552 trw_layer_calculate_bounds_waypoints ( vtl );
3553 vik_layers_panel_emit_update ( vlp );
3557 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3559 vtl->current_track = vik_track_new();
3560 vik_track_set_defaults ( vtl->current_track );
3561 vtl->current_track->visible = TRUE;
3562 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3563 // Create track with the preferred colour from the layer properties
3564 vtl->current_track->color = vtl->track_color;
3566 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3567 vtl->current_track->has_color = TRUE;
3568 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3571 static void trw_layer_new_track ( gpointer lav[2] )
3573 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3575 if ( ! vtl->current_track ) {
3576 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3577 new_track_create_common ( vtl, name );
3580 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3584 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3586 vtl->current_track = vik_track_new();
3587 vik_track_set_defaults ( vtl->current_track );
3588 vtl->current_track->visible = TRUE;
3589 vtl->current_track->is_route = TRUE;
3590 // By default make all routes red
3591 vtl->current_track->has_color = TRUE;
3592 gdk_color_parse ( "red", &vtl->current_track->color );
3593 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3596 static void trw_layer_new_route ( gpointer lav[2] )
3598 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3600 if ( ! vtl->current_track ) {
3601 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3602 new_route_create_common ( vtl, name );
3604 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3608 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3610 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3611 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3613 if ( g_hash_table_size (vtl->routes) > 0 ) {
3614 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3615 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3616 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3617 vik_layers_panel_emit_update ( vlp );
3622 static void trw_layer_finish_track ( gpointer lav[2] )
3624 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3625 vtl->current_track = NULL;
3626 vik_layer_emit_update ( VIK_LAYER(vtl) );
3629 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3631 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3632 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3634 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3635 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3636 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3637 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3638 vik_layers_panel_emit_update ( vlp );
3642 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3644 /* NB do not care if wp is visible or not */
3645 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3648 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3650 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3651 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3653 /* Only 1 waypoint - jump straight to it */
3654 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3655 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3656 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3658 /* If at least 2 waypoints - find center and then zoom to fit */
3659 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3661 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3662 maxmin[0].lat = vtl->waypoints_bbox.north;
3663 maxmin[1].lat = vtl->waypoints_bbox.south;
3664 maxmin[0].lon = vtl->waypoints_bbox.east;
3665 maxmin[1].lon = vtl->waypoints_bbox.west;
3666 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3669 vik_layers_panel_emit_update ( vlp );
3672 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3674 static gpointer pass_along[2];
3676 GtkWidget *export_submenu;
3677 pass_along[0] = vtl;
3678 pass_along[1] = vlp;
3680 item = gtk_menu_item_new();
3681 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3682 gtk_widget_show ( item );
3684 if ( vtl->current_track ) {
3685 if ( vtl->current_track->is_route )
3686 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3688 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3689 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3690 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3691 gtk_widget_show ( item );
3694 item = gtk_menu_item_new ();
3695 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3696 gtk_widget_show ( item );
3699 /* Now with icons */
3700 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3701 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3702 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3703 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3704 gtk_widget_show ( item );
3706 GtkWidget *view_submenu = gtk_menu_new();
3707 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3708 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3709 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3710 gtk_widget_show ( item );
3711 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3713 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3714 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3715 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3716 gtk_widget_show ( item );
3718 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3719 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3720 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3721 gtk_widget_show ( item );
3723 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3724 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3725 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3726 gtk_widget_show ( item );
3728 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3729 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3730 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3731 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3732 gtk_widget_show ( item );
3734 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3735 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3736 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3737 gtk_widget_show ( item );
3739 export_submenu = gtk_menu_new ();
3740 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3741 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3742 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3743 gtk_widget_show ( item );
3744 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3746 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3747 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3748 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3749 gtk_widget_show ( item );
3751 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3752 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3753 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3754 gtk_widget_show ( item );
3756 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3758 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3759 gtk_widget_show ( item );
3761 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3763 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3764 gtk_widget_show ( item );
3766 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3767 item = gtk_menu_item_new_with_mnemonic ( external1 );
3768 g_free ( external1 );
3769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3770 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3771 gtk_widget_show ( item );
3773 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3774 item = gtk_menu_item_new_with_mnemonic ( external2 );
3775 g_free ( external2 );
3776 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3777 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3778 gtk_widget_show ( item );
3780 GtkWidget *new_submenu = gtk_menu_new();
3781 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3782 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3783 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3784 gtk_widget_show(item);
3785 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3787 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3788 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3789 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3790 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3791 gtk_widget_show ( item );
3793 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3794 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3795 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3796 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3797 gtk_widget_show ( item );
3798 // Make it available only when a new track *not* already in progress
3799 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3801 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3802 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3803 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3804 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3805 gtk_widget_show ( item );
3806 // Make it available only when a new track *not* already in progress
3807 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3809 #ifdef VIK_CONFIG_GEOTAG
3810 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3811 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3812 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3813 gtk_widget_show ( item );
3816 GtkWidget *acquire_submenu = gtk_menu_new ();
3817 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3818 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3819 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3820 gtk_widget_show ( item );
3821 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3823 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3825 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3826 gtk_widget_show ( item );
3828 /* FIXME: only add menu when at least a routing engine has support for Directions */
3829 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3830 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3831 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3832 gtk_widget_show ( item );
3834 #ifdef VIK_CONFIG_OPENSTREETMAP
3835 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3836 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3837 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3838 gtk_widget_show ( item );
3840 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3842 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3843 gtk_widget_show ( item );
3846 #ifdef VIK_CONFIG_GEONAMES
3847 GtkWidget *wikipedia_submenu = gtk_menu_new();
3848 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3849 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3850 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3851 gtk_widget_show(item);
3852 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3854 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3855 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3856 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3857 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3858 gtk_widget_show ( item );
3860 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3861 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3863 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3864 gtk_widget_show ( item );
3867 #ifdef VIK_CONFIG_GEOCACHES
3868 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3869 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3870 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3871 gtk_widget_show ( item );
3874 #ifdef VIK_CONFIG_GEOTAG
3875 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3877 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3878 gtk_widget_show ( item );
3881 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3882 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3883 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3884 gtk_widget_show ( item );
3886 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3888 GtkWidget *upload_submenu = gtk_menu_new ();
3889 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3890 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3891 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3892 gtk_widget_show ( item );
3893 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3895 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3896 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3898 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3899 gtk_widget_show ( item );
3901 #ifdef VIK_CONFIG_OPENSTREETMAP
3902 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3903 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3904 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3905 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3906 gtk_widget_show ( item );
3909 GtkWidget *delete_submenu = gtk_menu_new ();
3910 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3911 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3912 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3913 gtk_widget_show ( item );
3914 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3916 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3917 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3918 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3919 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3920 gtk_widget_show ( item );
3922 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3923 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3924 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3925 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3926 gtk_widget_show ( item );
3928 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3929 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3930 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3931 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3932 gtk_widget_show ( item );
3934 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3935 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3937 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3938 gtk_widget_show ( item );
3940 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3941 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3942 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3943 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3944 gtk_widget_show ( item );
3946 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3947 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3948 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3949 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3950 gtk_widget_show ( item );
3952 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3953 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3955 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3956 gtk_widget_show ( item );
3959 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3960 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3962 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3963 gtk_widget_show ( item );
3966 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
3967 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3968 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
3969 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3970 gtk_widget_show ( item );
3971 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
3974 // Fake Waypoint UUIDs vi simple increasing integer
3975 static guint wp_uuid = 0;
3977 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3981 vik_waypoint_set_name (wp, name);
3983 if ( VIK_LAYER(vtl)->realized )
3985 // Do we need to create the sublayer:
3986 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3987 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3990 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3992 // Visibility column always needed for waypoints
3993 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 );
3995 // Actual setting of visibility dependent on the waypoint
3996 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3998 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4000 // Sort now as post_read is not called on a realized waypoint
4001 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4004 highest_wp_number_add_wp(vtl, name);
4005 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4009 // Fake Track UUIDs vi simple increasing integer
4010 static guint tr_uuid = 0;
4012 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4016 vik_track_set_name (t, name);
4018 if ( VIK_LAYER(vtl)->realized )
4020 // Do we need to create the sublayer:
4021 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4022 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4025 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4026 // Visibility column always needed for tracks
4027 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 );
4029 // Actual setting of visibility dependent on the track
4030 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4032 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4034 // Sort now as post_read is not called on a realized track
4035 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4038 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4040 trw_layer_update_treeview ( vtl, t );
4043 // Fake Route UUIDs vi simple increasing integer
4044 static guint rt_uuid = 0;
4046 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4050 vik_track_set_name (t, name);
4052 if ( VIK_LAYER(vtl)->realized )
4054 // Do we need to create the sublayer:
4055 if ( g_hash_table_size (vtl->routes) == 0 ) {
4056 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4059 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4060 // Visibility column always needed for routes
4061 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 );
4062 // Actual setting of visibility dependent on the route
4063 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4065 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4067 // Sort now as post_read is not called on a realized route
4068 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4071 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4073 trw_layer_update_treeview ( vtl, t );
4076 /* to be called whenever a track has been deleted or may have been changed. */
4077 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4079 if (vtl->current_tp_track == trk )
4080 trw_layer_cancel_current_tp ( vtl, FALSE );
4084 * Normally this is done to due the waypoint size preference having changed
4086 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4088 GHashTableIter iter;
4089 gpointer key, value;
4092 g_hash_table_iter_init ( &iter, vtl->waypoints );
4093 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4094 VikWaypoint *wp = VIK_WAYPOINT(value);
4096 // Reapply symbol setting to update the pixbuf
4097 gchar *tmp_symbol = g_strdup ( wp->symbol );
4098 vik_waypoint_set_symbol ( wp, tmp_symbol );
4099 g_free ( tmp_symbol );
4105 * trw_layer_new_unique_sublayer_name:
4107 * Allocates a unique new name
4109 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4112 gchar *newname = g_strdup(name);
4117 switch ( sublayer_type ) {
4118 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4119 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4121 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4122 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4125 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4128 // If found a name already in use try adding 1 to it and we try again
4130 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4132 newname = new_newname;
4135 } while ( id != NULL);
4140 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4142 // No more uniqueness of name forced when loading from a file
4143 // This now makes this function a little redunant as we just flow the parameters through
4144 vik_trw_layer_add_waypoint ( vtl, name, wp );
4147 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4149 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4150 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4151 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4152 vik_track_free ( tr );
4153 vtl->route_finder_append = FALSE; /* this means we have added it */
4156 // No more uniqueness of name forced when loading from a file
4158 vik_trw_layer_add_route ( vtl, name, tr );
4160 vik_trw_layer_add_track ( vtl, name, tr );
4162 if ( vtl->route_finder_check_added_track ) {
4163 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4164 vtl->route_finder_added_track = tr;
4169 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4171 *l = g_list_append(*l, id);
4175 * Move an item from one TRW layer to another TRW layer
4177 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4179 // TODO reconsider strategy when moving within layer (if anything...)
4180 gboolean rename = ( vtl_src != vtl_dest );
4184 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4185 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4189 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4191 newname = g_strdup ( trk->name );
4193 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4194 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4196 vik_trw_layer_delete_track ( vtl_src, trk );
4199 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4200 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4204 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4206 newname = g_strdup ( trk->name );
4208 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4209 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4211 vik_trw_layer_delete_route ( vtl_src, trk );
4214 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4215 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4219 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4221 newname = g_strdup ( wp->name );
4223 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4224 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4226 trw_layer_delete_waypoint ( vtl_src, wp );
4228 // Recalculate bounds even if not renamed as maybe dragged between layers
4229 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4230 trw_layer_calculate_bounds_waypoints ( vtl_src );
4234 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4236 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4237 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4239 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4240 GList *items = NULL;
4243 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4244 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4246 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4247 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4249 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4250 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4255 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4256 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4257 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4258 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4260 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4267 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4268 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4272 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4274 trku_udata *user_data = udata;
4275 if ( trk == user_data->trk ) {
4276 user_data->uuid = id;
4282 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4284 gboolean was_visible = FALSE;
4286 if ( trk && trk->name ) {
4288 if ( trk == vtl->current_track ) {
4289 vtl->current_track = NULL;
4290 vtl->current_tp_track = NULL;
4291 vtl->current_tp_id = NULL;
4292 vtl->moving_tp = FALSE;
4295 was_visible = trk->visible;
4297 if ( trk == vtl->route_finder_current_track )
4298 vtl->route_finder_current_track = NULL;
4300 if ( trk == vtl->route_finder_added_track )
4301 vtl->route_finder_added_track = NULL;
4307 // Hmmm, want key of it
4308 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4310 if ( trkf && udata.uuid ) {
4311 /* could be current_tp, so we have to check */
4312 trw_layer_cancel_tps_of_track ( vtl, trk );
4314 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4317 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4318 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4319 g_hash_table_remove ( vtl->tracks, udata.uuid );
4321 // If last sublayer, then remove sublayer container
4322 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4323 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4331 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4333 gboolean was_visible = FALSE;
4335 if ( trk && trk->name ) {
4337 if ( trk == vtl->current_track ) {
4338 vtl->current_track = NULL;
4339 vtl->current_tp_track = NULL;
4340 vtl->current_tp_id = NULL;
4341 vtl->moving_tp = FALSE;
4344 was_visible = trk->visible;
4346 if ( trk == vtl->route_finder_current_track )
4347 vtl->route_finder_current_track = NULL;
4349 if ( trk == vtl->route_finder_added_track )
4350 vtl->route_finder_added_track = NULL;
4356 // Hmmm, want key of it
4357 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4359 if ( trkf && udata.uuid ) {
4360 /* could be current_tp, so we have to check */
4361 trw_layer_cancel_tps_of_track ( vtl, trk );
4363 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4366 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4367 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4368 g_hash_table_remove ( vtl->routes, udata.uuid );
4370 // If last sublayer, then remove sublayer container
4371 if ( g_hash_table_size (vtl->routes) == 0 ) {
4372 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4380 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4382 gboolean was_visible = FALSE;
4384 if ( wp && wp->name ) {
4386 if ( wp == vtl->current_wp ) {
4387 vtl->current_wp = NULL;
4388 vtl->current_wp_id = NULL;
4389 vtl->moving_wp = FALSE;
4392 was_visible = wp->visible;
4398 // Hmmm, want key of it
4399 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4401 if ( wpf && udata.uuid ) {
4402 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4405 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4406 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4408 highest_wp_number_remove_wp(vtl, wp->name);
4409 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4411 // If last sublayer, then remove sublayer container
4412 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4413 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4423 // Only for temporary use by trw_layer_delete_waypoint_by_name
4424 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4426 wpu_udata *user_data = udata;
4427 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4428 user_data->uuid = id;
4435 * Delete a waypoint by the given name
4436 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4437 * as there be multiple waypoints with the same name
4439 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4442 // Fake a waypoint with the given name
4443 udata.wp = vik_waypoint_new ();
4444 vik_waypoint_set_name (udata.wp, name);
4445 // Currently only the name is used in this waypoint find function
4448 // Hmmm, want key of it
4449 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4451 vik_waypoint_free (udata.wp);
4453 if ( wpf && udata.uuid )
4454 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4460 VikTrack *trk; // input
4461 gpointer uuid; // output
4464 // Only for temporary use by trw_layer_delete_track_by_name
4465 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4467 tpu_udata *user_data = udata;
4468 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4469 user_data->uuid = id;
4476 * Delete a track by the given name
4477 * NOTE: ATM this will delete the first encountered Track with the specified name
4478 * as there may be multiple tracks with the same name within the specified hash table
4480 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4483 // Fake a track with the given name
4484 udata.trk = vik_track_new ();
4485 vik_track_set_name (udata.trk, name);
4486 // Currently only the name is used in this waypoint find function
4489 // Hmmm, want key of it
4490 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4492 vik_track_free (udata.trk);
4494 if ( trkf && udata.uuid ) {
4495 // This could be a little better written...
4496 if ( vtl->tracks == ht_tracks )
4497 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4498 if ( vtl->routes == ht_tracks )
4499 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4506 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4508 vik_treeview_item_delete (vt, it );
4511 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4514 vtl->current_track = NULL;
4515 vtl->route_finder_current_track = NULL;
4516 vtl->route_finder_added_track = NULL;
4517 if (vtl->current_tp_track)
4518 trw_layer_cancel_current_tp(vtl, FALSE);
4520 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4521 g_hash_table_remove_all(vtl->routes_iters);
4522 g_hash_table_remove_all(vtl->routes);
4524 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4526 vik_layer_emit_update ( VIK_LAYER(vtl) );
4529 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4532 vtl->current_track = NULL;
4533 vtl->route_finder_current_track = NULL;
4534 vtl->route_finder_added_track = NULL;
4535 if (vtl->current_tp_track)
4536 trw_layer_cancel_current_tp(vtl, FALSE);
4538 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4539 g_hash_table_remove_all(vtl->tracks_iters);
4540 g_hash_table_remove_all(vtl->tracks);
4542 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4544 vik_layer_emit_update ( VIK_LAYER(vtl) );
4547 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4549 vtl->current_wp = NULL;
4550 vtl->current_wp_id = NULL;
4551 vtl->moving_wp = FALSE;
4553 highest_wp_number_reset(vtl);
4555 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4556 g_hash_table_remove_all(vtl->waypoints_iters);
4557 g_hash_table_remove_all(vtl->waypoints);
4559 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4561 vik_layer_emit_update ( VIK_LAYER(vtl) );
4564 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4566 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4567 // Get confirmation from the user
4568 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4569 _("Are you sure you want to delete all tracks in %s?"),
4570 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4571 vik_trw_layer_delete_all_tracks (vtl);
4574 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4576 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4577 // Get confirmation from the user
4578 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4579 _("Are you sure you want to delete all routes in %s?"),
4580 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4581 vik_trw_layer_delete_all_routes (vtl);
4584 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4586 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4587 // Get confirmation from the user
4588 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4589 _("Are you sure you want to delete all waypoints in %s?"),
4590 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4591 vik_trw_layer_delete_all_waypoints (vtl);
4594 static void trw_layer_delete_item ( gpointer pass_along[6] )
4596 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4597 gboolean was_visible = FALSE;
4598 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4600 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4601 if ( wp && wp->name ) {
4602 if ( GPOINTER_TO_INT ( pass_along[4]) )
4603 // Get confirmation from the user
4604 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4605 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4606 _("Are you sure you want to delete the waypoint \"%s\"?"),
4609 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4612 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4614 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4615 if ( trk && trk->name ) {
4616 if ( GPOINTER_TO_INT ( pass_along[4]) )
4617 // Get confirmation from the user
4618 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4619 _("Are you sure you want to delete the track \"%s\"?"),
4622 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4627 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4628 if ( trk && trk->name ) {
4629 if ( GPOINTER_TO_INT ( pass_along[4]) )
4630 // Get confirmation from the user
4631 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4632 _("Are you sure you want to delete the route \"%s\"?"),
4635 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4639 vik_layer_emit_update ( VIK_LAYER(vtl) );
4643 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4645 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4647 vik_waypoint_set_name ( wp, new_name );
4649 // Now update the treeview as well
4654 // Need key of it for treeview update
4655 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4657 if ( wpf && udataU.uuid ) {
4658 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4661 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4662 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4667 static void trw_layer_properties_item ( gpointer pass_along[7] )
4669 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4670 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4672 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4674 if ( wp && wp->name )
4676 gboolean updated = FALSE;
4677 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4679 trw_layer_waypoint_rename ( vtl, wp, new_name );
4681 if ( updated && pass_along[6] )
4682 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4684 if ( updated && VIK_LAYER(vtl)->visible )
4685 vik_layer_emit_update ( VIK_LAYER(vtl) );
4691 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4692 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4694 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4696 if ( tr && tr->name )
4698 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4701 pass_along[1], /* vlp */
4702 pass_along[5], /* vvp */
4709 * trw_layer_track_statistics:
4711 * Show track statistics.
4712 * ATM jump to the stats page in the properties
4713 * TODO: consider separating the stats into an individual dialog?
4715 static void trw_layer_track_statistics ( gpointer pass_along[7] )
4717 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4719 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4720 trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4722 trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4724 if ( trk && trk->name ) {
4725 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4728 pass_along[1], // vlp
4729 pass_along[5], // vvp
4735 * Update the treeview of the track id - primarily to update the icon
4737 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4743 gpointer *trkf = NULL;
4744 if ( trk->is_route )
4745 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4747 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4749 if ( trkf && udata.uuid ) {
4751 GtkTreeIter *iter = NULL;
4752 if ( trk->is_route )
4753 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4755 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4758 // TODO: Make this a function
4759 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4760 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4761 ((trk->color.green & 0xff00) << 8) |
4762 (trk->color.blue & 0xff00);
4763 gdk_pixbuf_fill ( pixbuf, pixel );
4764 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4765 g_object_unref (pixbuf);
4772 Parameter 1 -> VikLayersPanel
4773 Parameter 2 -> VikLayer
4774 Parameter 3 -> VikViewport
4776 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4779 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4780 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4783 /* since vlp not set, vl & vvp should be valid instead! */
4785 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4786 vik_layer_emit_update ( VIK_LAYER(vl) );
4791 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4793 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4795 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4796 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4798 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4800 if ( track && track->trackpoints )
4801 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4804 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4806 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4808 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4809 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4811 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4813 if ( track && track->trackpoints )
4815 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4817 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4818 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4819 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4820 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4821 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4825 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4827 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4829 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4830 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4832 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4837 // Converting a track to a route can be a bit more complicated,
4838 // so give a chance to change our minds:
4839 if ( !trk->is_route &&
4840 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4841 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4843 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4844 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4849 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4852 trk_copy->is_route = !trk_copy->is_route;
4854 // ATM can't set name to self - so must create temporary copy
4855 gchar *name = g_strdup ( trk_copy->name );
4857 // Delete old one and then add new one
4858 if ( trk->is_route ) {
4859 vik_trw_layer_delete_route ( vtl, trk );
4860 vik_trw_layer_add_track ( vtl, name, trk_copy );
4863 // Extra route conversion bits...
4864 vik_track_merge_segments ( trk_copy );
4865 vik_track_to_routepoints ( trk_copy );
4867 vik_trw_layer_delete_track ( vtl, trk );
4868 vik_trw_layer_add_route ( vtl, name, trk_copy );
4872 // Update in case color of track / route changes when moving between sublayers
4873 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4876 static void trw_layer_anonymize_times ( gpointer pass_along[6] )
4878 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4880 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4881 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4883 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4886 vik_track_anonymize_times ( track );
4889 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4891 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4893 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4894 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4896 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4901 vtl->current_track = track;
4902 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);
4904 if ( track->trackpoints )
4905 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4909 * extend a track using route finder
4911 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4913 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4914 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4917 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4919 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4920 vtl->route_finder_coord = last_coord;
4921 vtl->route_finder_current_track = track;
4922 vtl->route_finder_started = TRUE;
4924 if ( track->trackpoints )
4925 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4932 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4934 // If have a vlp then perform a basic test to see if any DEM info available...
4936 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
4938 if ( !g_list_length(dems) ) {
4939 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
4947 * apply_dem_data_common:
4949 * A common function for applying the DEM values and reporting the results.
4951 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
4953 if ( !trw_layer_dem_test ( vtl, vlp ) )
4956 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
4957 // Inform user how much was changed
4959 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4960 g_snprintf(str, 64, tmp_str, changed);
4961 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4964 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
4966 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4968 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4969 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4971 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4974 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
4977 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
4979 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4981 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4982 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4984 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4987 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
4993 * A common function for applying the elevation smoothing and reporting the results.
4995 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
4997 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
4998 // Inform user how much was changed
5000 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5001 g_snprintf(str, 64, tmp_str, changed);
5002 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5008 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
5010 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5012 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5013 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5015 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5020 smooth_it ( vtl, track, FALSE );
5023 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
5025 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5027 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5028 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5030 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5035 smooth_it ( vtl, track, TRUE );
5039 * Commonal helper function
5041 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5044 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5045 g_snprintf(str, 64, tmp_str, changed);
5046 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5049 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
5051 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5052 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5054 if ( !trw_layer_dem_test ( vtl, vlp ) )
5058 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5060 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5062 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5066 GHashTableIter iter;
5067 gpointer key, value;
5069 g_hash_table_iter_init ( &iter, vtl->waypoints );
5070 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5071 VikWaypoint *wp = VIK_WAYPOINT(value);
5072 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5075 wp_changed_message ( vtl, changed );
5078 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
5080 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5081 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5083 if ( !trw_layer_dem_test ( vtl, vlp ) )
5087 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5089 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5091 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5095 GHashTableIter iter;
5096 gpointer key, value;
5098 g_hash_table_iter_init ( &iter, vtl->waypoints );
5099 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5100 VikWaypoint *wp = VIK_WAYPOINT(value);
5101 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5104 wp_changed_message ( vtl, changed );
5107 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
5109 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5111 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5112 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5114 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5119 GList *trps = track->trackpoints;
5122 trps = g_list_last(trps);
5123 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
5126 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
5128 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5130 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5131 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5133 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5138 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5141 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5144 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
5146 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5148 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5149 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5151 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5156 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5159 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5162 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
5164 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5166 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5167 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5169 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5174 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5177 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5181 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5183 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
5185 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5187 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5188 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5190 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5192 if ( trk && trk->trackpoints )
5194 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5195 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5196 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5197 if ( pass_along[1] )
5198 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5200 vik_layer_emit_update ( VIK_LAYER(vtl) );
5205 * Refine the selected track/route with a routing engine.
5206 * The routing engine is selected by the user, when requestiong the job.
5208 static void trw_layer_route_refine ( gpointer pass_along[6] )
5210 static gint last_engine = 0;
5211 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5214 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5215 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5217 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5219 if ( trk && trk->trackpoints )
5221 /* Check size of the route */
5222 int nb = vik_track_get_tp_count(trk);
5224 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5225 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5226 GTK_MESSAGE_WARNING,
5227 GTK_BUTTONS_OK_CANCEL,
5228 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5230 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5231 gtk_widget_destroy ( dialog );
5232 if (response != GTK_RESPONSE_OK )
5235 /* Select engine from dialog */
5236 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5237 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5238 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5240 GTK_RESPONSE_REJECT,
5242 GTK_RESPONSE_ACCEPT,
5244 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5245 gtk_widget_show_all(label);
5247 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5249 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5250 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5251 gtk_widget_show_all(combo);
5253 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5255 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5257 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5259 /* Dialog validated: retrieve selected engine and do the job */
5260 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5261 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5264 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5266 /* Force saving track */
5267 /* FIXME: remove or rename this hack */
5268 vtl->route_finder_check_added_track = TRUE;
5271 vik_routing_engine_refine (routing, vtl, trk);
5273 /* FIXME: remove or rename this hack */
5274 if ( vtl->route_finder_added_track )
5275 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5277 vtl->route_finder_added_track = NULL;
5278 vtl->route_finder_check_added_track = FALSE;
5280 vik_layer_emit_update ( VIK_LAYER(vtl) );
5282 /* Restore cursor */
5283 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5285 gtk_widget_destroy ( dialog );
5289 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5291 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5292 trw_layer_tpwin_init ( vtl );
5295 /*************************************
5296 * merge/split by time routines
5297 *************************************/
5299 /* called for each key in track hash table.
5300 * If the current track has the same time stamp type, add it to the result,
5301 * except the one pointed by "exclude".
5302 * set exclude to NULL if there is no exclude to check.
5303 * Note that the result is in reverse (for performance reasons).
5308 gboolean with_timestamps;
5310 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5312 twt_udata *user_data = udata;
5313 VikTrackpoint *p1, *p2;
5315 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5319 if (VIK_TRACK(value)->trackpoints) {
5320 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5321 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5323 if ( user_data->with_timestamps ) {
5324 if (!p1->has_timestamp || !p2->has_timestamp) {
5329 // Don't add tracks with timestamps when getting non timestamp tracks
5330 if (p1->has_timestamp || p2->has_timestamp) {
5336 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5339 /* called for each key in track hash table. if original track user_data[1] is close enough
5340 * to the passed one, add it to list in user_data[0]
5342 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5345 VikTrackpoint *p1, *p2;
5346 VikTrack *trk = VIK_TRACK(value);
5348 GList **nearby_tracks = ((gpointer *)user_data)[0];
5349 GList *tpoints = ((gpointer *)user_data)[1];
5352 * detect reasons for not merging, and return
5353 * if no reason is found not to merge, then do it.
5356 // Exclude the original track from the compiled list
5357 if (trk->trackpoints == tpoints) {
5361 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5362 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5364 if (trk->trackpoints) {
5365 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5366 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5368 if (!p1->has_timestamp || !p2->has_timestamp) {
5369 //g_print("no timestamp\n");
5373 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5374 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5375 if (! (abs(t1 - p2->timestamp) < threshold ||
5377 abs(p1->timestamp - t2) < threshold)
5384 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5387 /* comparison function used to sort tracks; a and b are hash table keys */
5388 /* Not actively used - can be restored if needed
5389 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5391 GHashTable *tracks = user_data;
5394 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5395 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5397 if (t1 < t2) return -1;
5398 if (t1 > t2) return 1;
5403 /* comparison function used to sort trackpoints */
5404 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5406 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5408 if (t1 < t2) return -1;
5409 if (t1 > t2) return 1;
5414 * comparison function which can be used to sort tracks or waypoints by name
5416 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5418 const gchar* namea = (const gchar*) a;
5419 const gchar* nameb = (const gchar*) b;
5420 if ( namea == NULL || nameb == NULL)
5423 // Same sort method as used in the vik_treeview_*_alphabetize functions
5424 return strcmp ( namea, nameb );
5428 * Attempt to merge selected track with other tracks specified by the user
5429 * Tracks to merge with must be of the same 'type' as the selected track -
5430 * either all with timestamps, or all without timestamps
5432 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5434 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5435 GList *other_tracks = NULL;
5436 GHashTable *ght_tracks;
5437 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5438 ght_tracks = vtl->routes;
5440 ght_tracks = vtl->tracks;
5442 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5447 if ( !track->trackpoints )
5451 udata.result = &other_tracks;
5452 udata.exclude = track->trackpoints;
5453 // Allow merging with 'similar' time type time tracks
5454 // i.e. either those times, or those without
5455 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5457 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5458 other_tracks = g_list_reverse(other_tracks);
5460 if ( !other_tracks ) {
5461 if ( udata.with_timestamps )
5462 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5464 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5468 // Sort alphabetically for user presentation
5469 // Convert into list of names for usage with dialog function
5470 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5471 GList *other_tracks_names = NULL;
5472 GList *iter = g_list_first ( other_tracks );
5474 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5475 iter = g_list_next ( iter );
5478 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5480 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5484 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5485 g_list_free(other_tracks);
5486 g_list_free(other_tracks_names);
5491 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5492 VikTrack *merge_track;
5493 if ( track->is_route )
5494 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5496 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5499 vik_track_steal_and_append_trackpoints ( track, merge_track );
5500 if ( track->is_route )
5501 vik_trw_layer_delete_route (vtl, merge_track);
5503 vik_trw_layer_delete_track (vtl, merge_track);
5504 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5507 for (l = merge_list; l != NULL; l = g_list_next(l))
5509 g_list_free(merge_list);
5511 vik_layer_emit_update( VIK_LAYER(vtl) );
5515 // c.f. trw_layer_sorted_track_id_by_name_list
5516 // but don't add the specified track to the list (normally current track)
5517 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5519 twt_udata *user_data = udata;
5522 if (trk->trackpoints == user_data->exclude) {
5526 // Sort named list alphabetically
5527 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5531 * Join - this allows combining 'tracks' and 'track routes'
5532 * i.e. doesn't care about whether tracks have consistent timestamps
5533 * ATM can only append one track at a time to the currently selected track
5535 static void trw_layer_append_track ( gpointer pass_along[6] )
5538 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5540 GHashTable *ght_tracks;
5541 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5542 ght_tracks = vtl->routes;
5544 ght_tracks = vtl->tracks;
5546 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5551 GList *other_tracks_names = NULL;
5553 // Sort alphabetically for user presentation
5554 // Convert into list of names for usage with dialog function
5555 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5557 udata.result = &other_tracks_names;
5558 udata.exclude = trk->trackpoints;
5560 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5562 // Note the limit to selecting one track only
5563 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5564 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5565 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5568 trk->is_route ? _("Append Route"): _("Append Track"),
5569 trk->is_route ? _("Select the route to append after the current route") :
5570 _("Select the track to append after the current track") );
5572 g_list_free(other_tracks_names);
5574 // It's a list, but shouldn't contain more than one other track!
5575 if ( append_list ) {
5577 for (l = append_list; l != NULL; l = g_list_next(l)) {
5578 // TODO: at present this uses the first track found by name,
5579 // which with potential multiple same named tracks may not be the one selected...
5580 VikTrack *append_track;
5581 if ( trk->is_route )
5582 append_track = vik_trw_layer_get_route ( vtl, l->data );
5584 append_track = vik_trw_layer_get_track ( vtl, l->data );
5586 if ( append_track ) {
5587 vik_track_steal_and_append_trackpoints ( trk, append_track );
5588 if ( trk->is_route )
5589 vik_trw_layer_delete_route (vtl, append_track);
5591 vik_trw_layer_delete_track (vtl, append_track);
5594 for (l = append_list; l != NULL; l = g_list_next(l))
5596 g_list_free(append_list);
5598 vik_layer_emit_update( VIK_LAYER(vtl) );
5603 * Very similar to trw_layer_append_track for joining
5604 * but this allows selection from the 'other' list
5605 * If a track is selected, then is shows routes and joins the selected one
5606 * If a route is selected, then is shows tracks and joins the selected one
5608 static void trw_layer_append_other ( gpointer pass_along[6] )
5611 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5613 GHashTable *ght_mykind, *ght_others;
5614 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5615 ght_mykind = vtl->routes;
5616 ght_others = vtl->tracks;
5619 ght_mykind = vtl->tracks;
5620 ght_others = vtl->routes;
5623 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5628 GList *other_tracks_names = NULL;
5630 // Sort alphabetically for user presentation
5631 // Convert into list of names for usage with dialog function
5632 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5634 udata.result = &other_tracks_names;
5635 udata.exclude = trk->trackpoints;
5637 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5639 // Note the limit to selecting one track only
5640 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5641 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5642 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5645 trk->is_route ? _("Append Track"): _("Append Route"),
5646 trk->is_route ? _("Select the track to append after the current route") :
5647 _("Select the route to append after the current track") );
5649 g_list_free(other_tracks_names);
5651 // It's a list, but shouldn't contain more than one other track!
5652 if ( append_list ) {
5654 for (l = append_list; l != NULL; l = g_list_next(l)) {
5655 // TODO: at present this uses the first track found by name,
5656 // which with potential multiple same named tracks may not be the one selected...
5658 // Get FROM THE OTHER TYPE list
5659 VikTrack *append_track;
5660 if ( trk->is_route )
5661 append_track = vik_trw_layer_get_track ( vtl, l->data );
5663 append_track = vik_trw_layer_get_route ( vtl, l->data );
5665 if ( append_track ) {
5667 if ( !append_track->is_route &&
5668 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5669 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5671 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5672 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5673 vik_track_merge_segments ( append_track );
5674 vik_track_to_routepoints ( append_track );
5681 vik_track_steal_and_append_trackpoints ( trk, append_track );
5683 // Delete copied which is FROM THE OTHER TYPE list
5684 if ( trk->is_route )
5685 vik_trw_layer_delete_track (vtl, append_track);
5687 vik_trw_layer_delete_route (vtl, append_track);
5690 for (l = append_list; l != NULL; l = g_list_next(l))
5692 g_list_free(append_list);
5693 vik_layer_emit_update( VIK_LAYER(vtl) );
5697 /* merge by segments */
5698 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5700 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5701 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5702 guint segments = vik_track_merge_segments ( trk );
5703 // NB currently no need to redraw as segments not actually shown on the display
5704 // However inform the user of what happened:
5706 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5707 g_snprintf(str, 64, tmp_str, segments);
5708 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5711 /* merge by time routine */
5712 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5714 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5718 GList *tracks_with_timestamp = NULL;
5719 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5720 if (orig_trk->trackpoints &&
5721 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5722 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5727 udata.result = &tracks_with_timestamp;
5728 udata.exclude = orig_trk->trackpoints;
5729 udata.with_timestamps = TRUE;
5730 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5731 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5733 if (!tracks_with_timestamp) {
5734 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5737 g_list_free(tracks_with_timestamp);
5739 static guint threshold_in_minutes = 1;
5740 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5741 _("Merge Threshold..."),
5742 _("Merge when time between tracks less than:"),
5743 &threshold_in_minutes)) {
5747 // keep attempting to merge all tracks until no merges within the time specified is possible
5748 gboolean attempt_merge = TRUE;
5749 GList *nearby_tracks = NULL;
5751 static gpointer params[3];
5753 while ( attempt_merge ) {
5755 // Don't try again unless tracks have changed
5756 attempt_merge = FALSE;
5758 trps = orig_trk->trackpoints;
5762 if (nearby_tracks) {
5763 g_list_free(nearby_tracks);
5764 nearby_tracks = NULL;
5767 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5768 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5770 /* g_print("Original track times: %d and %d\n", t1, t2); */
5771 params[0] = &nearby_tracks;
5772 params[1] = (gpointer)trps;
5773 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5775 /* get a list of adjacent-in-time tracks */
5776 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5779 GList *l = nearby_tracks;
5782 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5783 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5785 t1 = get_first_trackpoint(l)->timestamp;
5786 t2 = get_last_trackpoint(l)->timestamp;
5787 #undef get_first_trackpoint
5788 #undef get_last_trackpoint
5789 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5792 /* remove trackpoints from merged track, delete track */
5793 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5794 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5796 // Tracks have changed, therefore retry again against all the remaining tracks
5797 attempt_merge = TRUE;
5802 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5805 g_list_free(nearby_tracks);
5807 vik_layer_emit_update( VIK_LAYER(vtl) );
5811 * Split a track at the currently selected trackpoint
5813 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5815 if ( !vtl->current_tpl )
5818 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5819 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5821 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5822 GList *newglist = g_list_alloc ();
5823 newglist->prev = NULL;
5824 newglist->next = vtl->current_tpl->next;
5825 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5826 tr->trackpoints = newglist;
5828 vtl->current_tpl->next->prev = newglist; /* end old track here */
5829 vtl->current_tpl->next = NULL;
5831 // Bounds of the selected track changed due to the split
5832 vik_track_calculate_bounds ( vtl->current_tp_track );
5834 vtl->current_tpl = newglist; /* change tp to first of new track. */
5835 vtl->current_tp_track = tr;
5838 vik_trw_layer_add_route ( vtl, name, tr );
5840 vik_trw_layer_add_track ( vtl, name, tr );
5842 // Bounds of the new track created by the split
5843 vik_track_calculate_bounds ( tr );
5849 // Also need id of newly created track
5852 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5854 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5856 if ( trkf && udata.uuid )
5857 vtl->current_tp_id = udata.uuid;
5859 vtl->current_tp_id = NULL;
5861 vik_layer_emit_update(VIK_LAYER(vtl));
5867 /* split by time routine */
5868 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5870 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5871 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5872 GList *trps = track->trackpoints;
5874 GList *newlists = NULL;
5875 GList *newtps = NULL;
5876 static guint thr = 1;
5883 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5884 _("Split Threshold..."),
5885 _("Split when time between trackpoints exceeds:"),
5890 /* iterate through trackpoints, and copy them into new lists without touching original list */
5891 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5895 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5897 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5900 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5901 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5902 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5904 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5909 if (ts - prev_ts > thr*60) {
5910 /* flush accumulated trackpoints into new list */
5911 newlists = g_list_append(newlists, g_list_reverse(newtps));
5915 /* accumulate trackpoint copies in newtps, in reverse order */
5916 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5918 iter = g_list_next(iter);
5921 newlists = g_list_append(newlists, g_list_reverse(newtps));
5924 /* put lists of trackpoints into tracks */
5926 // Only bother updating if the split results in new tracks
5927 if (g_list_length (newlists) > 1) {
5932 tr = vik_track_copy ( track, FALSE );
5933 tr->trackpoints = (GList *)(iter->data);
5935 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5936 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5937 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5938 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5939 g_free ( new_tr_name );
5940 vik_track_calculate_bounds ( tr );
5941 iter = g_list_next(iter);
5943 // Remove original track and then update the display
5944 vik_trw_layer_delete_track (vtl, track);
5945 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5947 g_list_free(newlists);
5951 * Split a track by the number of points as specified by the user
5953 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5955 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5957 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5958 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5960 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5965 // Check valid track
5966 GList *trps = track->trackpoints;
5970 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5971 _("Split Every Nth Point"),
5972 _("Split on every Nth point:"),
5973 250, // Default value as per typical limited track capacity of various GPS devices
5977 // Was a valid number returned?
5983 GList *newlists = NULL;
5984 GList *newtps = NULL;
5989 /* accumulate trackpoint copies in newtps, in reverse order */
5990 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5992 if (count >= points) {
5993 /* flush accumulated trackpoints into new list */
5994 newlists = g_list_append(newlists, g_list_reverse(newtps));
5998 iter = g_list_next(iter);
6001 // If there is a remaining chunk put that into the new split list
6002 // This may well be the whole track if no split points were encountered
6004 newlists = g_list_append(newlists, g_list_reverse(newtps));
6007 /* put lists of trackpoints into tracks */
6009 // Only bother updating if the split results in new tracks
6010 if (g_list_length (newlists) > 1) {
6015 tr = vik_track_copy ( track, FALSE );
6016 tr->trackpoints = (GList *)(iter->data);
6018 if ( track->is_route ) {
6019 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6020 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6023 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6024 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6026 g_free ( new_tr_name );
6027 vik_track_calculate_bounds ( tr );
6029 iter = g_list_next(iter);
6031 // Remove original track and then update the display
6032 if ( track->is_route )
6033 vik_trw_layer_delete_route (vtl, track);
6035 vik_trw_layer_delete_track (vtl, track);
6036 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6038 g_list_free(newlists);
6042 * Split a track at the currently selected trackpoint
6044 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
6046 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6047 gint subtype = GPOINTER_TO_INT (pass_along[2]);
6048 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6052 * Split a track by its segments
6053 * Routes do not have segments so don't call this for routes
6055 static void trw_layer_split_segments ( gpointer pass_along[6] )
6057 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6058 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6065 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6068 for ( i = 0; i < ntracks; i++ ) {
6070 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6071 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6072 g_free ( new_tr_name );
6077 // Remove original track
6078 vik_trw_layer_delete_track ( vtl, trk );
6079 vik_layer_emit_update ( VIK_LAYER(vtl) );
6082 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6085 /* end of split/merge routines */
6087 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6091 // Find available adjacent trackpoint
6092 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6093 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6094 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6096 // Delete current trackpoint
6097 vik_trackpoint_free ( vtl->current_tpl->data );
6098 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6100 // Set to current to the available adjacent trackpoint
6101 vtl->current_tpl = new_tpl;
6103 if ( vtl->current_tp_track ) {
6104 vik_track_calculate_bounds ( vtl->current_tp_track );
6108 // Delete current trackpoint
6109 vik_trackpoint_free ( vtl->current_tpl->data );
6110 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6111 trw_layer_cancel_current_tp ( vtl, FALSE );
6116 * Delete the selected point
6118 static void trw_layer_delete_point_selected ( gpointer pass_along[6] )
6120 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6122 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6123 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6125 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6130 if ( !vtl->current_tpl )
6133 trw_layer_trackpoint_selected_delete ( vtl, trk );
6135 // Track has been updated so update tps:
6136 trw_layer_cancel_tps_of_track ( vtl, trk );
6138 vik_layer_emit_update ( VIK_LAYER(vtl) );
6142 * Delete adjacent track points at the same position
6143 * AKA Delete Dulplicates on the Properties Window
6145 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
6147 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6149 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6150 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6152 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6157 gulong removed = vik_track_remove_dup_points ( trk );
6159 // Track has been updated so update tps:
6160 trw_layer_cancel_tps_of_track ( vtl, trk );
6162 // Inform user how much was deleted as it's not obvious from the normal view
6164 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6165 g_snprintf(str, 64, tmp_str, removed);
6166 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6168 vik_layer_emit_update ( VIK_LAYER(vtl) );
6172 * Delete adjacent track points with the same timestamp
6173 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6175 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
6177 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6179 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6180 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6182 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6187 gulong removed = vik_track_remove_same_time_points ( trk );
6189 // Track has been updated so update tps:
6190 trw_layer_cancel_tps_of_track ( vtl, trk );
6192 // Inform user how much was deleted as it's not obvious from the normal view
6194 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6195 g_snprintf(str, 64, tmp_str, removed);
6196 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6198 vik_layer_emit_update ( VIK_LAYER(vtl) );
6204 static void trw_layer_insert_point_after ( gpointer pass_along[6] )
6206 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6208 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6209 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6211 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6216 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6218 vik_layer_emit_update ( VIK_LAYER(vtl) );
6221 static void trw_layer_insert_point_before ( gpointer pass_along[6] )
6223 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6225 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6226 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6228 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6233 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6235 vik_layer_emit_update ( VIK_LAYER(vtl) );
6241 static void trw_layer_reverse ( gpointer pass_along[6] )
6243 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6245 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6246 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6248 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6253 vik_track_reverse ( track );
6255 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
6259 * Similar to trw_layer_enum_item, but this uses a sorted method
6262 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6264 GList **list = (GList**)udata;
6265 // *list = g_list_prepend(*all, key); //unsorted method
6266 // Sort named list alphabetically
6267 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6272 * Now Waypoint specific sort
6274 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6276 GList **list = (GList**)udata;
6277 // Sort named list alphabetically
6278 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6282 * Track specific sort
6284 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6286 GList **list = (GList**)udata;
6287 // Sort named list alphabetically
6288 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6293 gboolean has_same_track_name;
6294 const gchar *same_track_name;
6295 } same_track_name_udata;
6297 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6299 const gchar* namea = (const gchar*) aa;
6300 const gchar* nameb = (const gchar*) bb;
6303 gint result = strcmp ( namea, nameb );
6305 if ( result == 0 ) {
6306 // Found two names the same
6307 same_track_name_udata *user_data = udata;
6308 user_data->has_same_track_name = TRUE;
6309 user_data->same_track_name = namea;
6312 // Leave ordering the same
6317 * Find out if any tracks have the same name in this hash table
6319 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6321 // Sort items by name, then compare if any next to each other are the same
6323 GList *track_names = NULL;
6324 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6327 if ( ! track_names )
6330 same_track_name_udata udata;
6331 udata.has_same_track_name = FALSE;
6333 // Use sort routine to traverse list comparing items
6334 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6335 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6336 // Still no tracks...
6340 return udata.has_same_track_name;
6344 * Force unqiue track names for the track table specified
6345 * Note the panel is a required parameter to enable the update of the names displayed
6346 * Specify if on tracks or else on routes
6348 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6350 // . Search list for an instance of repeated name
6351 // . get track of this name
6352 // . create new name
6353 // . rename track & update equiv. treeview iter
6354 // . repeat until all different
6356 same_track_name_udata udata;
6358 GList *track_names = NULL;
6359 udata.has_same_track_name = FALSE;
6360 udata.same_track_name = NULL;
6362 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6365 if ( ! track_names )
6368 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6370 // Still no tracks...
6371 if ( ! dummy_list1 )
6374 while ( udata.has_same_track_name ) {
6376 // Find a track with the same name
6379 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6381 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6385 g_critical("Houston, we've had a problem.");
6386 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6387 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6392 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6393 vik_track_set_name ( trk, newname );
6399 // Need want key of it for treeview update
6400 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6402 if ( trkf && udataU.uuid ) {
6406 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6408 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6411 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6413 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6415 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6419 // Start trying to find same names again...
6421 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6422 udata.has_same_track_name = FALSE;
6423 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6425 // No tracks any more - give up searching
6426 if ( ! dummy_list2 )
6427 udata.has_same_track_name = FALSE;
6431 vik_layers_panel_emit_update ( vlp );
6434 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6436 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6439 switch (GPOINTER_TO_INT (pass_along[2])) {
6440 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6441 iter = &(vtl->tracks_iter);
6442 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6444 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6445 iter = &(vtl->routes_iter);
6446 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6448 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6449 iter = &(vtl->waypoints_iter);
6450 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6454 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6457 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6459 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6462 switch (GPOINTER_TO_INT (pass_along[2])) {
6463 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6464 iter = &(vtl->tracks_iter);
6465 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6467 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6468 iter = &(vtl->routes_iter);
6469 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6471 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6472 iter = &(vtl->waypoints_iter);
6473 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6477 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6483 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6485 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6488 // Ensure list of track names offered is unique
6489 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6490 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6491 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6492 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6498 // Sort list alphabetically for better presentation
6499 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6502 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6506 // Get list of items to delete from the user
6507 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6510 _("Delete Selection"),
6511 _("Select tracks to delete"));
6514 // Delete requested tracks
6515 // since specificly requested, IMHO no need for extra confirmation
6516 if ( delete_list ) {
6518 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6519 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6520 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6522 g_list_free(delete_list);
6523 vik_layer_emit_update( VIK_LAYER(vtl) );
6530 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6532 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6535 // Ensure list of track names offered is unique
6536 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6537 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6538 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6539 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6545 // Sort list alphabetically for better presentation
6546 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6549 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6553 // Get list of items to delete from the user
6554 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6557 _("Delete Selection"),
6558 _("Select routes to delete") );
6561 // Delete requested routes
6562 // since specificly requested, IMHO no need for extra confirmation
6563 if ( delete_list ) {
6565 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6566 // This deletes first route it finds of that name (but uniqueness is enforced above)
6567 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6569 g_list_free(delete_list);
6570 vik_layer_emit_update( VIK_LAYER(vtl) );
6575 gboolean has_same_waypoint_name;
6576 const gchar *same_waypoint_name;
6577 } same_waypoint_name_udata;
6579 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6581 const gchar* namea = (const gchar*) aa;
6582 const gchar* nameb = (const gchar*) bb;
6585 gint result = strcmp ( namea, nameb );
6587 if ( result == 0 ) {
6588 // Found two names the same
6589 same_waypoint_name_udata *user_data = udata;
6590 user_data->has_same_waypoint_name = TRUE;
6591 user_data->same_waypoint_name = namea;
6594 // Leave ordering the same
6599 * Find out if any waypoints have the same name in this layer
6601 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6603 // Sort items by name, then compare if any next to each other are the same
6605 GList *waypoint_names = NULL;
6606 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6609 if ( ! waypoint_names )
6612 same_waypoint_name_udata udata;
6613 udata.has_same_waypoint_name = FALSE;
6615 // Use sort routine to traverse list comparing items
6616 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6617 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6618 // Still no waypoints...
6622 return udata.has_same_waypoint_name;
6626 * Force unqiue waypoint names for this layer
6627 * Note the panel is a required parameter to enable the update of the names displayed
6629 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6631 // . Search list for an instance of repeated name
6632 // . get waypoint of this name
6633 // . create new name
6634 // . rename waypoint & update equiv. treeview iter
6635 // . repeat until all different
6637 same_waypoint_name_udata udata;
6639 GList *waypoint_names = NULL;
6640 udata.has_same_waypoint_name = FALSE;
6641 udata.same_waypoint_name = NULL;
6643 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6646 if ( ! waypoint_names )
6649 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6651 // Still no waypoints...
6652 if ( ! dummy_list1 )
6655 while ( udata.has_same_waypoint_name ) {
6657 // Find a waypoint with the same name
6658 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6662 g_critical("Houston, we've had a problem.");
6663 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6664 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6669 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6671 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6673 // Start trying to find same names again...
6674 waypoint_names = NULL;
6675 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6676 udata.has_same_waypoint_name = FALSE;
6677 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6679 // No waypoints any more - give up searching
6680 if ( ! dummy_list2 )
6681 udata.has_same_waypoint_name = FALSE;
6685 vik_layers_panel_emit_update ( vlp );
6691 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6693 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6696 // Ensure list of waypoint names offered is unique
6697 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6698 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6699 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6700 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6706 // Sort list alphabetically for better presentation
6707 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6709 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6713 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6715 // Get list of items to delete from the user
6716 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6719 _("Delete Selection"),
6720 _("Select waypoints to delete"));
6723 // Delete requested waypoints
6724 // since specificly requested, IMHO no need for extra confirmation
6725 if ( delete_list ) {
6727 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6728 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6729 trw_layer_delete_waypoint_by_name (vtl, l->data);
6731 g_list_free(delete_list);
6733 trw_layer_calculate_bounds_waypoints ( vtl );
6734 vik_layer_emit_update( VIK_LAYER(vtl) );
6742 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6744 vik_treeview_item_toggle_visible ( vt, it );
6750 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6752 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6758 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6760 wp->visible = GPOINTER_TO_INT (on_off);
6766 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6768 wp->visible = !wp->visible;
6774 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6776 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6777 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6778 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6779 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6781 vik_layer_emit_update ( VIK_LAYER(vtl) );
6787 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6789 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6790 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6791 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6792 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6794 vik_layer_emit_update ( VIK_LAYER(vtl) );
6800 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6802 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6803 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6804 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6806 vik_layer_emit_update ( VIK_LAYER(vtl) );
6812 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6814 trk->visible = GPOINTER_TO_INT (on_off);
6820 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6822 trk->visible = !trk->visible;
6828 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6830 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6831 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6832 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6833 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6835 vik_layer_emit_update ( VIK_LAYER(vtl) );
6841 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6843 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6844 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6845 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6846 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6848 vik_layer_emit_update ( VIK_LAYER(vtl) );
6854 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6856 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6857 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6858 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6860 vik_layer_emit_update ( VIK_LAYER(vtl) );
6866 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6868 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6869 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6870 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6871 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6873 vik_layer_emit_update ( VIK_LAYER(vtl) );
6879 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6881 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6882 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6883 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6884 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6886 vik_layer_emit_update ( VIK_LAYER(vtl) );
6892 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6894 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6895 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6896 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6898 vik_layer_emit_update ( VIK_LAYER(vtl) );
6902 * trw_layer_analyse_close:
6904 * Stuff to do on dialog closure
6906 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6908 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6909 gtk_widget_destroy ( dialog );
6910 vtl->tracks_analysis_dialog = NULL;
6914 * vik_trw_layer_build_track_list_t:
6916 * Helper function to construct a list of #vik_trw_track_list_t
6918 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
6920 GList *tracks_and_layers = NULL;
6921 // build tracks_and_layers list
6923 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6924 vtdl->trk = VIK_TRACK(tracks->data);
6926 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6927 tracks = g_list_next ( tracks );
6929 return tracks_and_layers;
6933 * trw_layer_create_track_list:
6935 * Create the latest list of tracks with the associated layer(s)
6936 * Although this will always be from a single layer here
6938 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
6940 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6941 GList *tracks = NULL;
6942 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6943 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6945 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6947 return vik_trw_layer_build_track_list_t ( vtl, tracks );
6950 static void trw_layer_tracks_stats ( gpointer lav[2] )
6952 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6953 // There can only be one!
6954 if ( vtl->tracks_analysis_dialog )
6957 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6958 VIK_LAYER(vtl)->name,
6960 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6961 trw_layer_create_track_list,
6962 trw_layer_analyse_close );
6968 static void trw_layer_routes_stats ( gpointer lav[2] )
6970 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6971 // There can only be one!
6972 if ( vtl->tracks_analysis_dialog )
6975 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6976 VIK_LAYER(vtl)->name,
6978 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6979 trw_layer_create_track_list,
6980 trw_layer_analyse_close );
6983 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6985 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6987 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6990 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6992 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6995 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6996 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7000 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
7002 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7005 if ( !strncmp(wp->comment, "http", 4) ) {
7006 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
7007 } else if ( !strncmp(wp->description, "http", 4) ) {
7008 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
7012 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7014 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7016 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7018 // No actual change to the name supplied
7020 if (strcmp(newname, wp->name) == 0 )
7023 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7026 // An existing waypoint has been found with the requested name
7027 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7028 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7033 // Update WP name and refresh the treeview
7034 vik_waypoint_set_name (wp, newname);
7036 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7037 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7039 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7044 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7046 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7048 // No actual change to the name supplied
7050 if (strcmp(newname, trk->name) == 0)
7053 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7056 // An existing track has been found with the requested name
7057 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7058 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7062 // Update track name and refresh GUI parts
7063 vik_track_set_name (trk, newname);
7065 // Update any subwindows that could be displaying this track which has changed name
7066 // Only one Track Edit Window
7067 if ( l->current_tp_track == trk && l->tpwin ) {
7068 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7070 // Property Dialog of the track
7071 vik_trw_layer_propwin_update ( trk );
7073 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7074 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7076 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7081 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7083 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7085 // No actual change to the name supplied
7087 if (strcmp(newname, trk->name) == 0)
7090 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7093 // An existing track has been found with the requested name
7094 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7095 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7099 // Update track name and refresh GUI parts
7100 vik_track_set_name (trk, newname);
7102 // Update any subwindows that could be displaying this track which has changed name
7103 // Only one Track Edit Window
7104 if ( l->current_tp_track == trk && l->tpwin ) {
7105 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7107 // Property Dialog of the track
7108 vik_trw_layer_propwin_update ( trk );
7110 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7111 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7113 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7120 static gboolean is_valid_geocache_name ( gchar *str )
7122 gint len = strlen ( str );
7123 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]));
7126 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
7128 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7129 a_acquire_set_filter_track ( trk );
7132 #ifdef VIK_CONFIG_GOOGLE
7133 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7135 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7136 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7139 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
7141 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
7143 gchar *escaped = uri_escape ( tr->comment );
7144 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7145 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7152 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7153 /* viewpoint is now available instead */
7154 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7156 static gpointer pass_along[8];
7158 gboolean rv = FALSE;
7161 pass_along[1] = vlp;
7162 pass_along[2] = GINT_TO_POINTER (subtype);
7163 pass_along[3] = sublayer;
7164 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
7165 pass_along[5] = vvp;
7166 pass_along[6] = iter;
7167 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
7169 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7173 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7175 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7176 gtk_widget_show ( item );
7178 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7179 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7180 if (tr && tr->property_dialog)
7181 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7183 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7184 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7185 if (tr && tr->property_dialog)
7186 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7189 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7190 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7191 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7192 gtk_widget_show ( item );
7194 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7196 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7197 gtk_widget_show ( item );
7199 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7201 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7202 gtk_widget_show ( item );
7204 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7206 gboolean separator_created = FALSE;
7208 /* could be a right-click using the tool */
7209 if ( vlp != NULL ) {
7210 item = gtk_menu_item_new ();
7211 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7212 gtk_widget_show ( item );
7214 separator_created = TRUE;
7216 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7217 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7219 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7220 gtk_widget_show ( item );
7223 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7225 if ( wp && wp->name ) {
7226 if ( is_valid_geocache_name ( wp->name ) ) {
7228 if ( !separator_created ) {
7229 item = gtk_menu_item_new ();
7230 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7231 gtk_widget_show ( item );
7232 separator_created = TRUE;
7235 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7236 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7237 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7238 gtk_widget_show ( item );
7242 if ( wp && wp->image )
7244 if ( !separator_created ) {
7245 item = gtk_menu_item_new ();
7246 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7247 gtk_widget_show ( item );
7248 separator_created = TRUE;
7251 // Set up image paramater
7252 pass_along[5] = wp->image;
7254 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7255 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
7256 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7257 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7258 gtk_widget_show ( item );
7260 #ifdef VIK_CONFIG_GEOTAG
7261 GtkWidget *geotag_submenu = gtk_menu_new ();
7262 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7263 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7264 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7265 gtk_widget_show ( item );
7266 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7268 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7269 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7270 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7271 gtk_widget_show ( item );
7273 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7274 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7275 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7276 gtk_widget_show ( item );
7282 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7283 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7284 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7287 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7288 gtk_widget_show ( item );
7295 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7296 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7297 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7298 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7299 gtk_widget_show ( item );
7300 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7301 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7302 gtk_widget_set_sensitive ( item, TRUE );
7304 gtk_widget_set_sensitive ( item, FALSE );
7307 item = gtk_menu_item_new ();
7308 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7309 gtk_widget_show ( item );
7312 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7315 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7316 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7317 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7318 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7319 gtk_widget_show ( item );
7322 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7324 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7325 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7326 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7327 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7328 gtk_widget_show ( item );
7330 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7331 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7332 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7333 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7334 gtk_widget_show ( item );
7336 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7337 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7338 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7339 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7340 gtk_widget_show ( item );
7342 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7343 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7344 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7345 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7346 gtk_widget_show ( item );
7348 GtkWidget *vis_submenu = gtk_menu_new ();
7349 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7350 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7351 gtk_widget_show ( item );
7352 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7354 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7355 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7356 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7357 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7358 gtk_widget_show ( item );
7360 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7361 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7363 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7364 gtk_widget_show ( item );
7366 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7367 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7369 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7370 gtk_widget_show ( item );
7373 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7377 if ( l->current_track && !l->current_track->is_route ) {
7378 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7380 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7381 gtk_widget_show ( item );
7383 item = gtk_menu_item_new ();
7384 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7385 gtk_widget_show ( item );
7388 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7389 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7390 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7391 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7392 gtk_widget_show ( item );
7394 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7395 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7396 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7397 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7398 gtk_widget_show ( item );
7399 // Make it available only when a new track *not* already in progress
7400 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7402 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7403 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7404 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7405 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7406 gtk_widget_show ( item );
7408 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7409 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7410 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7411 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7412 gtk_widget_show ( item );
7414 GtkWidget *vis_submenu = gtk_menu_new ();
7415 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7416 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7417 gtk_widget_show ( item );
7418 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7420 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7421 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7423 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7424 gtk_widget_show ( item );
7426 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7427 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7428 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7429 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7430 gtk_widget_show ( item );
7432 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7433 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7434 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7435 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7437 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7438 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7439 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7440 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7442 gtk_widget_show ( item );
7444 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7445 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7446 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7447 gtk_widget_show ( item );
7450 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7454 if ( l->current_track && l->current_track->is_route ) {
7455 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7456 // Reuse finish track method
7457 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7458 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7459 gtk_widget_show ( item );
7461 item = gtk_menu_item_new ();
7462 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7463 gtk_widget_show ( item );
7466 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7467 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7468 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7469 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7470 gtk_widget_show ( item );
7472 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7473 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7474 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7475 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7476 gtk_widget_show ( item );
7477 // Make it available only when a new track *not* already in progress
7478 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7480 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7481 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7482 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), 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 Routes From Selection...") );
7487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7489 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7490 gtk_widget_show ( item );
7492 GtkWidget *vis_submenu = gtk_menu_new ();
7493 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7494 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7495 gtk_widget_show ( item );
7496 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7498 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7499 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7500 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7501 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7502 gtk_widget_show ( item );
7504 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), 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 ( _("_Toggle") );
7511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7512 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7513 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7515 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7516 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7517 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7518 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7520 gtk_widget_show ( item );
7522 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7524 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7525 gtk_widget_show ( item );
7529 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7530 GtkWidget *submenu_sort = gtk_menu_new ();
7531 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7532 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7533 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7534 gtk_widget_show ( item );
7535 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7537 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7538 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7539 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7540 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7541 gtk_widget_show ( item );
7543 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7544 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7546 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7547 gtk_widget_show ( item );
7550 GtkWidget *upload_submenu = gtk_menu_new ();
7552 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7554 item = gtk_menu_item_new ();
7555 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7556 gtk_widget_show ( item );
7558 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7559 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7560 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7561 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7562 if ( l->current_track ) {
7563 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7564 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7565 gtk_widget_show ( item );
7568 item = gtk_menu_item_new ();
7569 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7570 gtk_widget_show ( item );
7573 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7574 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7576 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7577 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7578 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7579 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7580 gtk_widget_show ( item );
7582 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7584 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7585 gtk_widget_show ( item );
7587 GtkWidget *goto_submenu;
7588 goto_submenu = gtk_menu_new ();
7589 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7590 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7591 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7592 gtk_widget_show ( item );
7593 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7595 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7596 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7597 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7598 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7599 gtk_widget_show ( item );
7601 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7602 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7604 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7605 gtk_widget_show ( item );
7607 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7608 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7609 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7610 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7611 gtk_widget_show ( item );
7613 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7614 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7615 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7616 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7617 gtk_widget_show ( item );
7619 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7620 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7621 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7622 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7623 gtk_widget_show ( item );
7625 // Routes don't have speeds
7626 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7627 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7628 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7630 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7631 gtk_widget_show ( item );
7634 GtkWidget *combine_submenu;
7635 combine_submenu = gtk_menu_new ();
7636 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7637 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7638 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7639 gtk_widget_show ( item );
7640 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7642 // Routes don't have times or segments...
7643 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7644 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7645 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7646 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7647 gtk_widget_show ( item );
7649 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7650 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7651 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7652 gtk_widget_show ( item );
7655 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7657 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7658 gtk_widget_show ( item );
7660 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7661 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7663 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7664 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7665 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7666 gtk_widget_show ( item );
7668 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7669 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7671 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7672 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7673 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7674 gtk_widget_show ( item );
7676 GtkWidget *split_submenu;
7677 split_submenu = gtk_menu_new ();
7678 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7679 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7680 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7681 gtk_widget_show ( item );
7682 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7684 // Routes don't have times or segments...
7685 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7686 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7687 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7688 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7689 gtk_widget_show ( item );
7691 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7692 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7693 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7694 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7695 gtk_widget_show ( item );
7698 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7700 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7701 gtk_widget_show ( item );
7703 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7705 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7706 gtk_widget_show ( item );
7707 // Make it available only when a trackpoint is selected.
7708 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7710 GtkWidget *insert_submenu = gtk_menu_new ();
7711 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7712 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7713 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7714 gtk_widget_show ( item );
7715 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7717 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7718 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7719 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7720 gtk_widget_show ( item );
7721 // Make it available only when a point is selected
7722 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7724 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7726 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7727 gtk_widget_show ( item );
7728 // Make it available only when a point is selected
7729 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7731 GtkWidget *delete_submenu;
7732 delete_submenu = gtk_menu_new ();
7733 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7734 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7735 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7736 gtk_widget_show ( item );
7737 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7739 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7740 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7741 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7742 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7743 gtk_widget_show ( item );
7744 // Make it available only when a point is selected
7745 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7747 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7748 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7749 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7750 gtk_widget_show ( item );
7752 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7754 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7755 gtk_widget_show ( item );
7757 GtkWidget *transform_submenu;
7758 transform_submenu = gtk_menu_new ();
7759 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7760 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7761 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7762 gtk_widget_show ( item );
7763 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7765 GtkWidget *dem_submenu;
7766 dem_submenu = gtk_menu_new ();
7767 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7768 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
7769 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7770 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7772 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7773 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7774 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7775 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7776 gtk_widget_show ( item );
7778 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7780 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7781 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7782 gtk_widget_show ( item );
7784 GtkWidget *smooth_submenu;
7785 smooth_submenu = gtk_menu_new ();
7786 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7787 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7788 gtk_widget_show ( item );
7789 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7791 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7792 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7793 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7794 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7795 gtk_widget_show ( item );
7797 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7798 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7799 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7800 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7801 gtk_widget_show ( item );
7803 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7804 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7806 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7807 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7809 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7810 gtk_widget_show ( item );
7812 // Routes don't have timestamps - so this is only available for tracks
7813 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7814 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7815 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7816 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7817 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7818 gtk_widget_show ( item );
7821 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7822 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7824 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7825 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7827 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7828 gtk_widget_show ( item );
7830 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7831 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7832 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7833 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7834 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7835 gtk_widget_show ( item );
7838 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7840 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7841 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7843 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7844 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
7845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7846 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7847 gtk_widget_show ( item );
7850 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7851 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7853 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7854 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7855 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7856 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7857 gtk_widget_show ( item );
7859 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7860 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7862 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7863 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7864 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7865 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7866 gtk_widget_show ( item );
7868 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7869 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7870 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
7871 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7872 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7873 gtk_widget_show ( item );
7876 // ATM can't upload a single waypoint but can do waypoints to a GPS
7877 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7878 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7879 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7880 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7881 gtk_widget_show ( item );
7882 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7884 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7885 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7886 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7887 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7888 gtk_widget_show ( item );
7892 #ifdef VIK_CONFIG_GOOGLE
7893 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7895 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7896 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7898 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7899 gtk_widget_show ( item );
7903 // Some things aren't usable with routes
7904 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7905 #ifdef VIK_CONFIG_OPENSTREETMAP
7906 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7907 // Convert internal pointer into actual track for usage outside this file
7908 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7909 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7911 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7912 gtk_widget_show ( item );
7915 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7916 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7917 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7918 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7919 gtk_widget_show ( item );
7921 /* ATM This function is only available via the layers panel, due to needing a vlp */
7923 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7924 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7925 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7927 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7928 gtk_widget_show ( item );
7932 #ifdef VIK_CONFIG_GEOTAG
7933 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7934 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7935 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7936 gtk_widget_show ( item );
7940 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7941 // Only show on viewport popmenu when a trackpoint is selected
7942 if ( ! vlp && l->current_tpl ) {
7944 item = gtk_menu_item_new ();
7945 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7946 gtk_widget_show ( item );
7948 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7949 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7950 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7951 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7952 gtk_widget_show ( item );
7956 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7957 GtkWidget *transform_submenu;
7958 transform_submenu = gtk_menu_new ();
7959 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7960 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7961 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7962 gtk_widget_show ( item );
7963 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7965 GtkWidget *dem_submenu;
7966 dem_submenu = gtk_menu_new ();
7967 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7968 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
7969 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7970 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7972 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
7974 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7975 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7976 gtk_widget_show ( item );
7978 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
7980 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7981 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7982 gtk_widget_show ( item );
7985 gtk_widget_show_all ( GTK_WIDGET(menu) );
7990 // TODO: Probably better to rework this track manipulation in viktrack.c
7991 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
7994 if (!vtl->current_tpl)
7997 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7998 VikTrackpoint *tp_other = NULL;
8001 if (!vtl->current_tpl->prev)
8003 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8005 if (!vtl->current_tpl->next)
8007 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8010 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8013 VikTrackpoint *tp_new = vik_trackpoint_new();
8014 struct LatLon ll_current, ll_other;
8015 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8016 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8018 /* main positional interpolation */
8019 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8020 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8022 /* Now other properties that can be interpolated */
8023 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8025 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8026 /* Note here the division is applied to each part, then added
8027 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8028 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8029 tp_new->has_timestamp = TRUE;
8032 if (tp_current->speed != NAN && tp_other->speed != NAN)
8033 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8035 /* TODO - improve interpolation of course, as it may not be correct.
8036 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8037 [similar applies if value is in radians] */
8038 if (tp_current->course != NAN && tp_other->course != NAN)
8039 tp_new->course = (tp_current->course + tp_other->course)/2;
8041 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8043 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8044 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8046 // Otherwise try routes
8047 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8051 gint index = g_list_index ( trk->trackpoints, tp_current );
8055 // NB no recalculation of bounds since it is inserted between points
8056 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8061 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8067 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8071 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8073 if ( vtl->current_tpl )
8075 vtl->current_tpl = NULL;
8076 vtl->current_tp_track = NULL;
8077 vtl->current_tp_id = NULL;
8078 vik_layer_emit_update(VIK_LAYER(vtl));
8082 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8084 g_assert ( vtl->tpwin != NULL );
8085 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8086 trw_layer_cancel_current_tp ( vtl, TRUE );
8088 if ( vtl->current_tpl == NULL )
8091 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8093 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8094 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8096 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8098 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8100 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8104 trw_layer_trackpoint_selected_delete ( vtl, tr );
8106 if ( vtl->current_tpl )
8107 // Reset dialog with the available adjacent trackpoint
8108 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8110 vik_layer_emit_update(VIK_LAYER(vtl));
8112 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8114 if ( vtl->current_tp_track )
8115 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8116 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8118 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8120 if ( vtl->current_tp_track )
8121 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8122 vik_layer_emit_update(VIK_LAYER(vtl));
8124 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8126 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8127 vik_layer_emit_update(VIK_LAYER(vtl));
8129 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8130 vik_layer_emit_update(VIK_LAYER(vtl));
8134 * trw_layer_dialog_shift:
8135 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8137 * Try to reposition a dialog if it's over the specified coord
8138 * so to not obscure the item of interest
8140 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8142 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8144 // Attempt force dialog to be shown so we can find out where it is more reliably...
8145 while ( gtk_events_pending() )
8146 gtk_main_iteration ();
8148 // get parent window position & size
8149 gint win_pos_x, win_pos_y;
8150 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8152 gint win_size_x, win_size_y;
8153 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8155 // get own dialog size
8156 gint dia_size_x, dia_size_y;
8157 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8159 // get own dialog position
8160 gint dia_pos_x, dia_pos_y;
8161 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8163 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8164 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8166 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8168 gint vp_xx, vp_yy; // In viewport pixels
8169 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8171 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8175 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8177 // Transform Viewport pixels into absolute pixels
8178 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8179 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8181 // Is dialog over the point (to within an ^^ edge value)
8182 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8183 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8187 gint hh = vik_viewport_get_height ( vvp );
8189 // Consider the difference in viewport to the full window
8190 gint offset_y = dest_y;
8191 // Add difference between dialog and window sizes
8192 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8194 if ( vp_yy > hh/2 ) {
8195 // Point in bottom half, move window to top half
8196 gtk_window_move ( dialog, dia_pos_x, offset_y );
8199 // Point in top half, move dialog down
8200 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8204 // Shift left<->right
8205 gint ww = vik_viewport_get_width ( vvp );
8207 // Consider the difference in viewport to the full window
8208 gint offset_x = dest_x;
8209 // Add difference between dialog and window sizes
8210 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8212 if ( vp_xx > ww/2 ) {
8213 // Point on right, move window to left
8214 gtk_window_move ( dialog, offset_x, dia_pos_y );
8217 // Point on left, move right
8218 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8226 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8230 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8231 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8232 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8233 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8235 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8237 if ( vtl->current_tpl ) {
8238 // get tp pixel position
8239 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8241 // Shift up<->down to try not to obscure the trackpoint.
8242 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8246 if ( vtl->current_tpl )
8247 if ( vtl->current_tp_track )
8248 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8249 /* set layer name and TP data */
8252 /***************************************************************************
8254 ***************************************************************************/
8256 /*** Utility data structures and functions ****/
8260 gint closest_x, closest_y;
8261 gboolean draw_images;
8262 gpointer *closest_wp_id;
8263 VikWaypoint *closest_wp;
8269 gint closest_x, closest_y;
8270 gpointer closest_track_id;
8271 VikTrackpoint *closest_tp;
8277 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8283 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8285 // If waypoint has an image then use the image size to select
8286 if ( params->draw_images && wp->image ) {
8287 gint slackx, slacky;
8288 slackx = wp->image_width / 2;
8289 slacky = wp->image_height / 2;
8291 if ( x <= params->x + slackx && x >= params->x - slackx
8292 && y <= params->y + slacky && y >= params->y - slacky ) {
8293 params->closest_wp_id = id;
8294 params->closest_wp = wp;
8295 params->closest_x = x;
8296 params->closest_y = y;
8299 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8300 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8301 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8303 params->closest_wp_id = id;
8304 params->closest_wp = wp;
8305 params->closest_x = x;
8306 params->closest_y = y;
8310 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8312 GList *tpl = t->trackpoints;
8318 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8324 tp = VIK_TRACKPOINT(tpl->data);
8326 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8328 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8329 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8330 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8332 params->closest_track_id = id;
8333 params->closest_tp = tp;
8334 params->closest_tpl = tpl;
8335 params->closest_x = x;
8336 params->closest_y = y;
8342 // ATM: Leave this as 'Track' only.
8343 // Not overly bothered about having a snap to route trackpoint capability
8344 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8346 TPSearchParams params;
8350 params.closest_track_id = NULL;
8351 params.closest_tp = NULL;
8352 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8353 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8354 return params.closest_tp;
8357 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8359 WPSearchParams params;
8363 params.draw_images = vtl->drawimages;
8364 params.closest_wp = NULL;
8365 params.closest_wp_id = NULL;
8366 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8367 return params.closest_wp;
8371 // Some forward declarations
8372 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8373 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8374 static void marker_end_move ( tool_ed_t *t );
8377 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8381 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8383 // Here always allow snapping back to the original location
8384 // this is useful when one decides not to move the thing afterall
8385 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8388 if ( event->state & GDK_CONTROL_MASK )
8390 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8392 new_coord = tp->coord;
8396 if ( event->state & GDK_SHIFT_MASK )
8398 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8400 new_coord = wp->coord;
8404 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8406 marker_moveto ( t, x, y );
8413 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8415 if ( t->holding && event->button == 1 )
8418 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8421 if ( event->state & GDK_CONTROL_MASK )
8423 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8425 new_coord = tp->coord;
8429 if ( event->state & GDK_SHIFT_MASK )
8431 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8433 new_coord = wp->coord;
8436 marker_end_move ( t );
8438 // Determine if working on a waypoint or a trackpoint
8439 if ( t->is_waypoint ) {
8440 // Update waypoint position
8441 vtl->current_wp->coord = new_coord;
8442 trw_layer_calculate_bounds_waypoints ( vtl );
8443 // Reset waypoint pointer
8444 vtl->current_wp = NULL;
8445 vtl->current_wp_id = NULL;
8448 if ( vtl->current_tpl ) {
8449 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8451 if ( vtl->current_tp_track )
8452 vik_track_calculate_bounds ( vtl->current_tp_track );
8455 if ( vtl->current_tp_track )
8456 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8457 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8461 vik_layer_emit_update ( VIK_LAYER(vtl) );
8468 Returns true if a waypoint or track is found near the requested event position for this particular layer
8469 The item found is automatically selected
8470 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8472 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8474 if ( event->button != 1 )
8477 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8480 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8484 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8486 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8488 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8489 WPSearchParams wp_params;
8490 wp_params.vvp = vvp;
8491 wp_params.x = event->x;
8492 wp_params.y = event->y;
8493 wp_params.draw_images = vtl->drawimages;
8494 wp_params.closest_wp_id = NULL;
8495 wp_params.closest_wp = NULL;
8497 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8499 if ( wp_params.closest_wp ) {
8502 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8504 // Too easy to move it so must be holding shift to start immediately moving it
8505 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8506 if ( event->state & GDK_SHIFT_MASK ||
8507 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8508 // Put into 'move buffer'
8509 // NB vvp & vw already set in tet
8510 tet->vtl = (gpointer)vtl;
8511 tet->is_waypoint = TRUE;
8513 marker_begin_move (tet, event->x, event->y);
8516 vtl->current_wp = wp_params.closest_wp;
8517 vtl->current_wp_id = wp_params.closest_wp_id;
8519 vik_layer_emit_update ( VIK_LAYER(vtl) );
8525 // Used for both track and route lists
8526 TPSearchParams tp_params;
8527 tp_params.vvp = vvp;
8528 tp_params.x = event->x;
8529 tp_params.y = event->y;
8530 tp_params.closest_track_id = NULL;
8531 tp_params.closest_tp = NULL;
8532 tp_params.closest_tpl = NULL;
8533 tp_params.bbox = bbox;
8535 if (vtl->tracks_visible) {
8536 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8538 if ( tp_params.closest_tp ) {
8540 // Always select + highlight the track
8541 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8543 tet->is_waypoint = FALSE;
8545 // Select the Trackpoint
8546 // Can move it immediately when control held or it's the previously selected tp
8547 if ( event->state & GDK_CONTROL_MASK ||
8548 vtl->current_tpl == tp_params.closest_tpl ) {
8549 // Put into 'move buffer'
8550 // NB vvp & vw already set in tet
8551 tet->vtl = (gpointer)vtl;
8552 marker_begin_move (tet, event->x, event->y);
8555 vtl->current_tpl = tp_params.closest_tpl;
8556 vtl->current_tp_id = tp_params.closest_track_id;
8557 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8559 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8562 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8564 vik_layer_emit_update ( VIK_LAYER(vtl) );
8569 // Try again for routes
8570 if (vtl->routes_visible) {
8571 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8573 if ( tp_params.closest_tp ) {
8575 // Always select + highlight the track
8576 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8578 tet->is_waypoint = FALSE;
8580 // Select the Trackpoint
8581 // Can move it immediately when control held or it's the previously selected tp
8582 if ( event->state & GDK_CONTROL_MASK ||
8583 vtl->current_tpl == tp_params.closest_tpl ) {
8584 // Put into 'move buffer'
8585 // NB vvp & vw already set in tet
8586 tet->vtl = (gpointer)vtl;
8587 marker_begin_move (tet, event->x, event->y);
8590 vtl->current_tpl = tp_params.closest_tpl;
8591 vtl->current_tp_id = tp_params.closest_track_id;
8592 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8594 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8597 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8599 vik_layer_emit_update ( VIK_LAYER(vtl) );
8604 /* these aren't the droids you're looking for */
8605 vtl->current_wp = NULL;
8606 vtl->current_wp_id = NULL;
8607 trw_layer_cancel_current_tp ( vtl, FALSE );
8610 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8615 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8617 if ( event->button != 3 )
8620 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8623 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8626 /* Post menu for the currently selected item */
8628 /* See if a track is selected */
8629 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8630 if ( track && track->visible ) {
8632 if ( track->name ) {
8634 if ( vtl->track_right_click_menu )
8635 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8637 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8644 if ( track->is_route )
8645 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8647 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8649 if ( trkf && udataU.uuid ) {
8652 if ( track->is_route )
8653 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8655 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8657 trw_layer_sublayer_add_menu_items ( vtl,
8658 vtl->track_right_click_menu,
8660 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8666 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8672 /* See if a waypoint is selected */
8673 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8674 if ( waypoint && waypoint->visible ) {
8675 if ( waypoint->name ) {
8677 if ( vtl->wp_right_click_menu )
8678 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8680 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8683 udata.wp = waypoint;
8686 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8688 if ( wpf && udata.uuid ) {
8689 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8691 trw_layer_sublayer_add_menu_items ( vtl,
8692 vtl->wp_right_click_menu,
8694 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8699 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8708 /* background drawing hook, to be passed the viewport */
8709 static gboolean tool_sync_done = TRUE;
8711 static gboolean tool_sync(gpointer data)
8713 VikViewport *vvp = data;
8714 gdk_threads_enter();
8715 vik_viewport_sync(vvp);
8716 tool_sync_done = TRUE;
8717 gdk_threads_leave();
8721 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8724 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8725 gdk_gc_set_function ( t->gc, GDK_INVERT );
8726 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8727 vik_viewport_sync(t->vvp);
8732 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8734 VikViewport *vvp = t->vvp;
8735 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8736 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8740 if (tool_sync_done) {
8741 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8742 tool_sync_done = FALSE;
8746 static void marker_end_move ( tool_ed_t *t )
8748 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8749 g_object_unref ( t->gc );
8753 /*** Edit waypoint ****/
8755 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8757 tool_ed_t *t = g_new(tool_ed_t, 1);
8763 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8768 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8770 WPSearchParams params;
8771 tool_ed_t *t = data;
8772 VikViewport *vvp = t->vvp;
8774 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8781 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8784 if ( vtl->current_wp && vtl->current_wp->visible )
8786 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8788 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8790 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8791 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8793 if ( event->button == 3 )
8794 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8796 marker_begin_move(t, event->x, event->y);
8803 params.x = event->x;
8804 params.y = event->y;
8805 params.draw_images = vtl->drawimages;
8806 params.closest_wp_id = NULL;
8807 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8808 params.closest_wp = NULL;
8809 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8810 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8812 // how do we get here?
8813 marker_begin_move(t, event->x, event->y);
8814 g_critical("shouldn't be here");
8817 else if ( params.closest_wp )
8819 if ( event->button == 3 )
8820 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8822 vtl->waypoint_rightclick = FALSE;
8824 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8826 vtl->current_wp = params.closest_wp;
8827 vtl->current_wp_id = params.closest_wp_id;
8829 /* could make it so don't update if old WP is off screen and new is null but oh well */
8830 vik_layer_emit_update ( VIK_LAYER(vtl) );
8834 vtl->current_wp = NULL;
8835 vtl->current_wp_id = NULL;
8836 vtl->waypoint_rightclick = FALSE;
8837 vik_layer_emit_update ( VIK_LAYER(vtl) );
8841 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8843 tool_ed_t *t = data;
8844 VikViewport *vvp = t->vvp;
8846 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8851 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8854 if ( event->state & GDK_CONTROL_MASK )
8856 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8858 new_coord = tp->coord;
8862 if ( event->state & GDK_SHIFT_MASK )
8864 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8865 if ( wp && wp != vtl->current_wp )
8866 new_coord = wp->coord;
8871 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8873 marker_moveto ( t, x, y );
8880 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8882 tool_ed_t *t = data;
8883 VikViewport *vvp = t->vvp;
8885 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8888 if ( t->holding && event->button == 1 )
8891 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8894 if ( event->state & GDK_CONTROL_MASK )
8896 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8898 new_coord = tp->coord;
8902 if ( event->state & GDK_SHIFT_MASK )
8904 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8905 if ( wp && wp != vtl->current_wp )
8906 new_coord = wp->coord;
8909 marker_end_move ( t );
8911 vtl->current_wp->coord = new_coord;
8913 trw_layer_calculate_bounds_waypoints ( vtl );
8914 vik_layer_emit_update ( VIK_LAYER(vtl) );
8917 /* PUT IN RIGHT PLACE!!! */
8918 if ( event->button == 3 && vtl->waypoint_rightclick )
8920 if ( vtl->wp_right_click_menu )
8921 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8922 if ( vtl->current_wp ) {
8923 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8924 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 );
8925 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8927 vtl->waypoint_rightclick = FALSE;
8932 /*** New track ****/
8934 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8941 GdkDrawable *drawable;
8947 * Draw specified pixmap
8949 static gboolean draw_sync ( gpointer data )
8951 draw_sync_t *ds = (draw_sync_t*) data;
8952 // Sometimes don't want to draw
8953 // normally because another update has taken precedent such as panning the display
8954 // which means this pixmap is no longer valid
8955 if ( ds->vtl->draw_sync_do ) {
8956 gdk_threads_enter();
8957 gdk_draw_drawable (ds->drawable,
8960 0, 0, 0, 0, -1, -1);
8961 ds->vtl->draw_sync_done = TRUE;
8962 gdk_threads_leave();
8968 static gchar* distance_string (gdouble distance)
8972 /* draw label with distance */
8973 vik_units_distance_t dist_units = a_vik_get_units_distance ();
8974 switch (dist_units) {
8975 case VIK_UNITS_DISTANCE_MILES:
8976 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8977 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8978 } else if (distance < 1609.4) {
8979 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8981 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8985 // VIK_UNITS_DISTANCE_KILOMETRES
8986 if (distance >= 1000 && distance < 100000) {
8987 g_sprintf(str, "%3.2f km", distance/1000.0);
8988 } else if (distance < 1000) {
8989 g_sprintf(str, "%d m", (int)distance);
8991 g_sprintf(str, "%d km", (int)distance/1000);
8995 return g_strdup (str);
8999 * Actually set the message in statusbar
9001 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9003 // Only show elevation data when track has some elevation properties
9004 gchar str_gain_loss[64];
9005 str_gain_loss[0] = '\0';
9006 gchar str_last_step[64];
9007 str_last_step[0] = '\0';
9008 gchar *str_total = distance_string (distance);
9010 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9011 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9012 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9014 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9017 if ( last_step > 0 ) {
9018 gchar *tmp = distance_string (last_step);
9019 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9023 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9025 // Write with full gain/loss information
9026 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9027 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9029 g_free ( str_total );
9033 * Figure out what information should be set in the statusbar and then write it
9035 static void update_statusbar ( VikTrwLayer *vtl )
9037 // Get elevation data
9038 gdouble elev_gain, elev_loss;
9039 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9041 /* Find out actual distance of current track */
9042 gdouble distance = vik_track_get_length (vtl->current_track);
9044 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9048 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9050 /* if we haven't sync'ed yet, we don't have time to do more. */
9051 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9052 GList *iter = g_list_last ( vtl->current_track->trackpoints );
9053 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
9055 static GdkPixmap *pixmap = NULL;
9057 // Need to check in case window has been resized
9058 w1 = vik_viewport_get_width(vvp);
9059 h1 = vik_viewport_get_height(vvp);
9061 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9063 gdk_drawable_get_size (pixmap, &w2, &h2);
9064 if (w1 != w2 || h1 != h2) {
9065 g_object_unref ( G_OBJECT ( pixmap ) );
9066 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9069 // Reset to background
9070 gdk_draw_drawable (pixmap,
9071 vtl->current_track_newpoint_gc,
9072 vik_viewport_get_pixmap(vvp),
9073 0, 0, 0, 0, -1, -1);
9075 draw_sync_t *passalong;
9078 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9080 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9081 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9082 // thus when we come to reset to the background it would include what we have already drawn!!
9083 gdk_draw_line ( pixmap,
9084 vtl->current_track_newpoint_gc,
9085 x1, y1, event->x, event->y );
9086 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9088 /* Find out actual distance of current track */
9089 gdouble distance = vik_track_get_length (vtl->current_track);
9091 // Now add distance to where the pointer is //
9094 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9095 vik_coord_to_latlon ( &coord, &ll );
9096 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9097 distance = distance + last_step;
9099 // Get elevation data
9100 gdouble elev_gain, elev_loss;
9101 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9103 // Adjust elevation data (if available) for the current pointer position
9105 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9106 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9107 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9108 // Adjust elevation of last track point
9109 if ( elev_new > last_tpt->altitude )
9111 elev_gain += elev_new - last_tpt->altitude;
9114 elev_loss += last_tpt->altitude - elev_new;
9119 // Display of the distance 'tooltip' during track creation is controlled by a preference
9121 if ( a_vik_get_create_track_tooltip() ) {
9123 gchar *str = distance_string (distance);
9125 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9126 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9127 pango_layout_set_text (pl, str, -1);
9129 pango_layout_get_pixel_size ( pl, &wd, &hd );
9132 // offset from cursor a bit depending on font size
9136 // Create a background block to make the text easier to read over the background map
9137 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9138 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9139 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9141 g_object_unref ( G_OBJECT ( pl ) );
9142 g_object_unref ( G_OBJECT ( background_block_gc ) );
9146 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9147 passalong->vtl = vtl;
9148 passalong->pixmap = pixmap;
9149 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9150 passalong->gc = vtl->current_track_newpoint_gc;
9154 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9156 // Update statusbar with full gain/loss information
9157 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9159 // draw pixmap when we have time to
9160 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9161 vtl->draw_sync_done = FALSE;
9162 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9164 return VIK_LAYER_TOOL_ACK;
9167 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9169 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9170 vtl->current_track = NULL;
9171 vik_layer_emit_update ( VIK_LAYER(vtl) );
9173 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9175 if ( vtl->current_track->trackpoints )
9177 GList *last = g_list_last(vtl->current_track->trackpoints);
9178 g_free ( last->data );
9179 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9182 update_statusbar ( vtl );
9184 vik_layer_emit_update ( VIK_LAYER(vtl) );
9191 * Common function to handle trackpoint button requests on either a route or a track
9192 * . enables adding a point via normal click
9193 * . enables removal of last point via right click
9194 * . finishing of the track or route via double clicking
9196 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9200 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9203 if ( event->button == 2 ) {
9204 // As the display is panning, the new track pixmap is now invalid so don't draw it
9205 // otherwise this drawing done results in flickering back to an old image
9206 vtl->draw_sync_do = FALSE;
9210 if ( event->button == 3 )
9212 if ( !vtl->current_track )
9215 if ( vtl->current_track->trackpoints )
9217 GList *last = g_list_last(vtl->current_track->trackpoints);
9218 g_free ( last->data );
9219 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9221 vik_track_calculate_bounds ( vtl->current_track );
9222 update_statusbar ( vtl );
9224 vik_layer_emit_update ( VIK_LAYER(vtl) );
9228 if ( event->type == GDK_2BUTTON_PRESS )
9230 /* subtract last (duplicate from double click) tp then end */
9231 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9233 GList *last = g_list_last(vtl->current_track->trackpoints);
9234 g_free ( last->data );
9235 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9236 /* undo last, then end */
9237 vtl->current_track = NULL;
9239 vik_layer_emit_update ( VIK_LAYER(vtl) );
9243 tp = vik_trackpoint_new();
9244 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9246 /* snap to other TP */
9247 if ( event->state & GDK_CONTROL_MASK )
9249 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9251 tp->coord = other_tp->coord;
9254 tp->newsegment = FALSE;
9255 tp->has_timestamp = FALSE;
9258 if ( vtl->current_track ) {
9259 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9260 /* Auto attempt to get elevation from DEM data (if it's available) */
9261 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9264 vtl->ct_x1 = vtl->ct_x2;
9265 vtl->ct_y1 = vtl->ct_y2;
9266 vtl->ct_x2 = event->x;
9267 vtl->ct_y2 = event->y;
9269 vik_layer_emit_update ( VIK_LAYER(vtl) );
9273 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9275 // ----------------------------------------------------- if current is a route - switch to new track
9276 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9278 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9279 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9281 new_track_create_common ( vtl, name );
9287 return tool_new_track_or_route_click ( vtl, event, vvp );
9290 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9292 if ( event->button == 2 ) {
9293 // Pan moving ended - enable potential point drawing again
9294 vtl->draw_sync_do = TRUE;
9295 vtl->draw_sync_done = TRUE;
9299 /*** New route ****/
9301 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9306 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9308 // -------------------------- if current is a track - switch to new route
9309 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9311 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9312 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9313 new_route_create_common ( vtl, name );
9319 return tool_new_track_or_route_click ( vtl, event, vvp );
9322 /*** New waypoint ****/
9324 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9329 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9332 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9334 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9335 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9336 trw_layer_calculate_bounds_waypoints ( vtl );
9337 vik_layer_emit_update ( VIK_LAYER(vtl) );
9343 /*** Edit trackpoint ****/
9345 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9347 tool_ed_t *t = g_new(tool_ed_t, 1);
9353 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9358 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9360 tool_ed_t *t = data;
9361 VikViewport *vvp = t->vvp;
9362 TPSearchParams params;
9363 /* OUTDATED DOCUMENTATION:
9364 find 5 pixel range on each side. then put these UTM, and a pointer
9365 to the winning track name (and maybe the winning track itself), and a
9366 pointer to the winning trackpoint, inside an array or struct. pass
9367 this along, do a foreach on the tracks which will do a foreach on the
9370 params.x = event->x;
9371 params.y = event->y;
9372 params.closest_track_id = NULL;
9373 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9374 params.closest_tp = NULL;
9375 params.closest_tpl = NULL;
9376 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9378 if ( event->button != 1 )
9381 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9384 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9387 if ( vtl->current_tpl )
9389 /* 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.) */
9390 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9391 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9396 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9398 if ( current_tr->visible &&
9399 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9400 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9401 marker_begin_move ( t, event->x, event->y );
9407 if ( vtl->tracks_visible )
9408 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9410 if ( params.closest_tp )
9412 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9413 vtl->current_tpl = params.closest_tpl;
9414 vtl->current_tp_id = params.closest_track_id;
9415 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9416 trw_layer_tpwin_init ( vtl );
9417 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9418 vik_layer_emit_update ( VIK_LAYER(vtl) );
9422 if ( vtl->routes_visible )
9423 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9425 if ( params.closest_tp )
9427 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9428 vtl->current_tpl = params.closest_tpl;
9429 vtl->current_tp_id = params.closest_track_id;
9430 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9431 trw_layer_tpwin_init ( vtl );
9432 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9433 vik_layer_emit_update ( VIK_LAYER(vtl) );
9437 /* these aren't the droids you're looking for */
9441 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9443 tool_ed_t *t = data;
9444 VikViewport *vvp = t->vvp;
9446 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9452 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9455 if ( event->state & GDK_CONTROL_MASK )
9457 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9458 if ( tp && tp != vtl->current_tpl->data )
9459 new_coord = tp->coord;
9461 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9464 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9465 marker_moveto ( t, x, y );
9473 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9475 tool_ed_t *t = data;
9476 VikViewport *vvp = t->vvp;
9478 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9480 if ( event->button != 1)
9485 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9488 if ( event->state & GDK_CONTROL_MASK )
9490 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9491 if ( tp && tp != vtl->current_tpl->data )
9492 new_coord = tp->coord;
9495 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9496 if ( vtl->current_tp_track )
9497 vik_track_calculate_bounds ( vtl->current_tp_track );
9499 marker_end_move ( t );
9501 /* diff dist is diff from orig */
9503 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9505 vik_layer_emit_update ( VIK_LAYER(vtl) );
9512 /*** Route Finder ***/
9513 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9518 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9521 if ( !vtl ) return FALSE;
9522 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9523 if ( event->button == 3 && vtl->route_finder_current_track ) {
9525 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9527 vtl->route_finder_coord = *new_end;
9529 vik_layer_emit_update ( VIK_LAYER(vtl) );
9530 /* remove last ' to:...' */
9531 if ( vtl->route_finder_current_track->comment ) {
9532 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9533 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9534 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9535 last_to - vtl->route_finder_current_track->comment - 1);
9536 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9541 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9542 struct LatLon start, end;
9544 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9545 vik_coord_to_latlon ( &(tmp), &end );
9546 vtl->route_finder_coord = tmp; /* for continuations */
9548 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9549 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9550 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9552 vtl->route_finder_check_added_track = TRUE;
9553 vtl->route_finder_started = FALSE;
9556 vik_routing_default_find ( vtl, start, end);
9558 /* see if anything was done -- a track was added or appended to */
9559 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9560 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 ) );
9561 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9562 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9563 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9564 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9567 if ( vtl->route_finder_added_track )
9568 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9570 vtl->route_finder_added_track = NULL;
9571 vtl->route_finder_check_added_track = FALSE;
9572 vtl->route_finder_append = FALSE;
9574 vik_layer_emit_update ( VIK_LAYER(vtl) );
9576 vtl->route_finder_started = TRUE;
9577 vtl->route_finder_coord = tmp;
9578 vtl->route_finder_current_track = NULL;
9583 /*** Show picture ****/
9585 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9590 /* Params are: vvp, event, last match found or NULL */
9591 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9593 if ( wp->image && wp->visible )
9595 gint x, y, slackx, slacky;
9596 GdkEventButton *event = (GdkEventButton *) params[1];
9598 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9599 slackx = wp->image_width / 2;
9600 slacky = wp->image_height / 2;
9601 if ( x <= event->x + slackx && x >= event->x - slackx
9602 && y <= event->y + slacky && y >= event->y - slacky )
9604 params[2] = wp->image; /* we've found a match. however continue searching
9605 * since we want to find the last match -- that
9606 * is, the match that was drawn last. */
9611 static void trw_layer_show_picture ( gpointer pass_along[6] )
9613 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9615 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9618 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9619 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9620 g_free ( quoted_file );
9621 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9623 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
9624 g_error_free ( err );
9627 #endif /* WINDOWS */
9630 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9632 gpointer params[3] = { vvp, event, NULL };
9633 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9635 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9638 static gpointer pass_along[6];
9639 pass_along[0] = vtl;
9640 pass_along[5] = params[2];
9641 trw_layer_show_picture ( pass_along );
9642 return TRUE; /* found a match */
9645 return FALSE; /* go through other layers, searching for a match */
9648 /***************************************************************************
9650 ***************************************************************************/
9653 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9655 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9656 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9659 /* Structure for thumbnail creating data used in the background thread */
9661 VikTrwLayer *vtl; // Layer needed for redrawing
9662 GSList *pics; // Image list
9663 } thumbnail_create_thread_data;
9665 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9667 guint total = g_slist_length(tctd->pics), done = 0;
9668 while ( tctd->pics )
9670 a_thumbnails_create ( (gchar *) tctd->pics->data );
9671 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9673 return -1; /* Abort thread */
9675 tctd->pics = tctd->pics->next;
9678 // Redraw to show the thumbnails as they are now created
9679 if ( IS_VIK_LAYER(tctd->vtl) )
9680 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9685 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9687 while ( tctd->pics )
9689 g_free ( tctd->pics->data );
9690 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9695 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9697 if ( ! vtl->has_verified_thumbnails )
9699 GSList *pics = NULL;
9700 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9703 gint len = g_slist_length ( pics );
9704 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9705 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9708 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9710 (vik_thr_func) create_thumbnails_thread,
9712 (vik_thr_free_func) thumbnail_create_thread_free,
9720 static const gchar* my_track_colors ( gint ii )
9722 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9734 // Fast and reliable way of returning a colour
9735 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9738 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9740 GHashTableIter iter;
9741 gpointer key, value;
9745 g_hash_table_iter_init ( &iter, vtl->tracks );
9747 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9749 // Tracks get a random spread of colours if not already assigned
9750 if ( ! VIK_TRACK(value)->has_color ) {
9751 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9752 VIK_TRACK(value)->color = vtl->track_color;
9754 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9756 VIK_TRACK(value)->has_color = TRUE;
9759 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9762 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9768 g_hash_table_iter_init ( &iter, vtl->routes );
9770 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9772 // Routes get an intermix of reds
9773 if ( ! VIK_TRACK(value)->has_color ) {
9775 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9777 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9778 VIK_TRACK(value)->has_color = TRUE;
9781 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9788 * (Re)Calculate the bounds of the waypoints in this layer,
9789 * This should be called whenever waypoints are changed
9791 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9793 struct LatLon topleft = { 0.0, 0.0 };
9794 struct LatLon bottomright = { 0.0, 0.0 };
9797 GHashTableIter iter;
9798 gpointer key, value;
9800 g_hash_table_iter_init ( &iter, vtl->waypoints );
9802 // Set bounds to first point
9803 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9804 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9805 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9808 // Ensure there is another point...
9809 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9811 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9813 // See if this point increases the bounds.
9814 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9816 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9817 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9818 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9819 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9823 vtl->waypoints_bbox.north = topleft.lat;
9824 vtl->waypoints_bbox.east = bottomright.lon;
9825 vtl->waypoints_bbox.south = bottomright.lat;
9826 vtl->waypoints_bbox.west = topleft.lon;
9829 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9831 vik_track_calculate_bounds ( trk );
9834 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9836 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9837 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9840 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9842 if ( ! VIK_LAYER(vtl)->vt )
9845 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9846 if ( g_hash_table_size (vtl->tracks) > 1 )
9847 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9849 if ( g_hash_table_size (vtl->routes) > 1 )
9850 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9852 if ( g_hash_table_size (vtl->waypoints) > 1 )
9853 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9856 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9858 if ( VIK_LAYER(vtl)->realized )
9859 trw_layer_verify_thumbnails ( vtl, vvp );
9860 trw_layer_track_alloc_colors ( vtl );
9862 trw_layer_calculate_bounds_waypoints ( vtl );
9863 trw_layer_calculate_bounds_tracks ( vtl );
9865 // Apply treeview sort after loading all the tracks for this layer
9866 // (rather than sorted insert on each individual track additional)
9867 // and after subsequent changes to the properties as the specified order may have changed.
9868 // since the sorting of a treeview section is now very quick
9869 // NB sorting is also performed after every name change as well to maintain the list order
9870 trw_layer_sort_all ( vtl );
9873 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9875 return vtl->coord_mode;
9879 * Uniquify the whole layer
9880 * Also requires the layers panel as the names shown there need updating too
9881 * Returns whether the operation was successful or not
9883 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9886 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9887 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9888 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9894 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9896 vik_coord_convert ( &(wp->coord), *dest_mode );
9899 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9901 vik_track_convert ( tr, *dest_mode );
9904 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9906 if ( vtl->coord_mode != dest_mode )
9908 vtl->coord_mode = dest_mode;
9909 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9910 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9911 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9915 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9917 vtl->menu_selection = selection;
9920 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9922 return (vtl->menu_selection);
9925 /* ----------- Downloading maps along tracks --------------- */
9927 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9929 /* TODO: calculating based on current size of viewport */
9930 const gdouble w_at_zoom_0_125 = 0.0013;
9931 const gdouble h_at_zoom_0_125 = 0.0011;
9932 gdouble zoom_factor = zoom_level/0.125;
9934 wh->lat = h_at_zoom_0_125 * zoom_factor;
9935 wh->lon = w_at_zoom_0_125 * zoom_factor;
9937 return 0; /* all OK */
9940 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9942 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9943 (dist->lat >= ABS(to->north_south - from->north_south)))
9946 VikCoord *coord = g_malloc(sizeof(VikCoord));
9947 coord->mode = VIK_COORD_LATLON;
9949 if (ABS(gradient) < 1) {
9950 if (from->east_west > to->east_west)
9951 coord->east_west = from->east_west - dist->lon;
9953 coord->east_west = from->east_west + dist->lon;
9954 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9956 if (from->north_south > to->north_south)
9957 coord->north_south = from->north_south - dist->lat;
9959 coord->north_south = from->north_south + dist->lat;
9960 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9966 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9968 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9969 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9971 VikCoord *next = from;
9973 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9975 list = g_list_prepend(list, next);
9981 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9983 typedef struct _Rect {
9988 #define GLRECT(iter) ((Rect *)((iter)->data))
9991 GList *rects_to_download = NULL;
9994 if (get_download_area_width(vvp, zoom_level, &wh))
9997 GList *iter = tr->trackpoints;
10001 gboolean new_map = TRUE;
10002 VikCoord *cur_coord, tl, br;
10005 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10007 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10008 rect = g_malloc(sizeof(Rect));
10011 rect->center = *cur_coord;
10012 rects_to_download = g_list_prepend(rects_to_download, rect);
10017 gboolean found = FALSE;
10018 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10019 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10030 GList *fillins = NULL;
10031 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10032 /* seems that ATM the function get_next_coord works only for LATLON */
10033 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10034 /* fill-ins for far apart points */
10035 GList *cur_rect, *next_rect;
10036 for (cur_rect = rects_to_download;
10037 (next_rect = cur_rect->next) != NULL;
10038 cur_rect = cur_rect->next) {
10039 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10040 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10041 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10045 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10048 GList *iter = fillins;
10050 cur_coord = (VikCoord *)(iter->data);
10051 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10052 rect = g_malloc(sizeof(Rect));
10055 rect->center = *cur_coord;
10056 rects_to_download = g_list_prepend(rects_to_download, rect);
10061 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10062 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10066 for (iter = fillins; iter; iter = iter->next)
10067 g_free(iter->data);
10068 g_list_free(fillins);
10070 if (rects_to_download) {
10071 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10072 g_free(rect_iter->data);
10073 g_list_free(rects_to_download);
10077 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
10081 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10082 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10083 gint selected_zoom, default_zoom;
10085 VikTrwLayer *vtl = pass_along[0];
10086 VikLayersPanel *vlp = pass_along[1];
10088 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10089 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
10091 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
10095 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10097 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10098 int num_maps = g_list_length(vmls);
10101 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10105 // Convert from list of vmls to list of names. Allowing the user to select one of them
10106 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10107 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10109 gchar **np = map_names;
10110 VikMapsLayer **lp = map_layers;
10112 for (i = 0; i < num_maps; i++) {
10113 vml = (VikMapsLayer *)(vmls->data);
10115 *np++ = vik_maps_layer_get_map_label(vml);
10118 // Mark end of the array lists
10122 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10123 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10124 if (cur_zoom == zoom_vals[default_zoom])
10127 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10129 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10132 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10135 for (i = 0; i < num_maps; i++)
10136 g_free(map_names[i]);
10138 g_free(map_layers);
10144 /**** lowest waypoint number calculation ***/
10145 static gint highest_wp_number_name_to_number(const gchar *name) {
10146 if ( strlen(name) == 3 ) {
10147 int n = atoi(name);
10148 if ( n < 100 && name[0] != '0' )
10150 if ( n < 10 && name[0] != '0' )
10158 static void highest_wp_number_reset(VikTrwLayer *vtl)
10160 vtl->highest_wp_number = -1;
10163 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10165 /* if is bigger that top, add it */
10166 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10167 if ( new_wp_num > vtl->highest_wp_number )
10168 vtl->highest_wp_number = new_wp_num;
10171 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10173 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10174 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10175 if ( vtl->highest_wp_number == old_wp_num ) {
10177 vtl->highest_wp_number--;
10179 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10180 /* search down until we find something that *does* exist */
10182 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10183 vtl->highest_wp_number--;
10184 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10189 /* get lowest unused number */
10190 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10193 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10195 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10196 return g_strdup(buf);
10200 * trw_layer_create_track_list_both:
10202 * Create the latest list of tracks and routes
10204 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10206 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10207 GList *tracks = NULL;
10208 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10209 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10211 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10214 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] )
10216 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
10218 gchar *title = NULL;
10219 if ( GPOINTER_TO_INT(pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10220 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10222 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10224 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), pass_along[2], trw_layer_create_track_list, FALSE );
10228 static void trw_layer_track_list_dialog ( gpointer lav[2] )
10230 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10231 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10233 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10234 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );