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 #ifdef VIK_CONFIG_GEOTAG
40 #include "viktrwlayer_geotag.h"
41 #include "geotag_exif.h"
43 #include "garminsymbols.h"
44 #include "thumbnails.h"
45 #include "background.h"
50 #include "geonamessearch.h"
51 #ifdef VIK_CONFIG_OPENSTREETMAP
52 #include "osm-traces.h"
55 #include "datasources.h"
56 #include "datasource_gps.h"
57 #include "vikexttool_datasources.h"
60 #include "vikrouting.h"
62 #include "icons/icons.h"
76 #include <gdk/gdkkeysyms.h>
78 #include <glib/gstdio.h>
79 #include <glib/gi18n.h>
81 #define VIK_TRW_LAYER_TRACK_GC 6
82 #define VIK_TRW_LAYER_TRACK_GCS 10
83 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
84 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
85 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
86 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
87 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
88 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
90 #define DRAWMODE_BY_TRACK 0
91 #define DRAWMODE_BY_SPEED 1
92 #define DRAWMODE_ALL_SAME_COLOR 2
93 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
94 // as we are (re)calculating the colour for every point
99 /* this is how it knows when you click if you are clicking close to a trackpoint. */
100 #define TRACKPOINT_SIZE_APPROX 5
101 #define WAYPOINT_SIZE_APPROX 5
103 #define MIN_STOP_LENGTH 15
104 #define MAX_STOP_LENGTH 86400
105 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
106 /* this is multiplied by user-inputted value from 1-100. */
108 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
110 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
112 FS_XX_SMALL = 0, // 'xx-small'
115 FS_MEDIUM, // DEFAULT
122 struct _VikTrwLayer {
125 GHashTable *tracks_iters;
127 GHashTable *routes_iters;
128 GHashTable *waypoints_iters;
129 GHashTable *waypoints;
130 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
131 gboolean tracks_visible, routes_visible, waypoints_visible;
132 LatLonBBox waypoints_bbox;
134 gboolean track_draw_labels;
137 guint8 drawpoints_size;
138 guint8 drawelevation;
139 guint8 elevation_factor;
143 guint8 drawdirections;
144 guint8 drawdirections_size;
145 guint8 line_thickness;
146 guint8 bg_line_thickness;
147 vik_layer_sort_order_t track_sort_order;
149 PangoLayout *tracklabellayout;
150 font_size_t track_font_size;
151 gchar *track_fsize_str;
155 gboolean wp_draw_symbols;
156 font_size_t wp_font_size;
158 vik_layer_sort_order_t wp_sort_order;
160 gdouble track_draw_speed_factor;
162 GdkGC *track_1color_gc;
163 GdkColor track_color;
164 GdkGC *current_track_gc;
165 // Separate GC for a track's potential new point as drawn via separate method
166 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
167 GdkGC *current_track_newpoint_gc;
168 GdkGC *track_bg_gc; GdkColor track_bg_color;
169 GdkGC *waypoint_gc; GdkColor waypoint_color;
170 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
171 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
173 GdkFont *waypoint_font;
174 VikTrack *current_track; // ATM shared between new tracks and new routes
175 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
176 gboolean draw_sync_done;
177 gboolean draw_sync_do;
179 VikCoordMode coord_mode;
181 /* wp editing tool */
182 VikWaypoint *current_wp;
183 gpointer current_wp_id;
185 gboolean waypoint_rightclick;
187 /* track editing tool */
189 VikTrack *current_tp_track;
190 gpointer current_tp_id;
191 VikTrwLayerTpwin *tpwin;
193 /* track editing tool -- more specifically, moving tps */
196 /* route finder tool */
197 gboolean route_finder_started;
198 VikCoord route_finder_coord;
199 gboolean route_finder_check_added_track;
200 VikTrack *route_finder_added_track;
201 VikTrack *route_finder_current_track;
202 gboolean route_finder_append;
209 guint16 image_cache_size;
211 /* for waypoint text */
212 PangoLayout *wplabellayout;
214 gboolean has_verified_thumbnails;
216 GtkMenu *wp_right_click_menu;
217 GtkMenu *track_right_click_menu;
220 VikStdLayerMenuItem menu_selection;
222 gint highest_wp_number;
225 GtkWidget *tracks_analysis_dialog;
228 /* A caached waypoint image. */
231 gchar *image; /* filename */
234 struct DrawingParams {
239 guint16 width, height;
240 gdouble cc; // Cosine factor in track directions
241 gdouble ss; // Sine factor in track directions
242 const VikCoord *center;
243 gboolean one_zone, lat_lon;
244 gdouble ce1, ce2, cn1, cn2;
248 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
250 static void trw_layer_delete_item ( gpointer pass_along[6] );
251 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
252 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
254 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
255 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
257 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
258 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
260 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
261 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
263 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl );
265 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
266 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
267 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
268 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
269 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
270 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
271 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
272 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
273 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
274 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
275 static void trw_layer_append_track ( gpointer pass_along[6] );
276 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
277 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
278 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
279 static void trw_layer_split_segments ( gpointer pass_along[6] );
280 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
281 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
282 static void trw_layer_reverse ( gpointer pass_along[6] );
283 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
284 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
285 static void trw_layer_show_picture ( gpointer pass_along[6] );
286 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
288 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
289 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
290 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
291 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
292 static void trw_layer_new_wp ( gpointer lav[2] );
293 static void trw_layer_new_track ( gpointer lav[2] );
294 static void trw_layer_new_route ( gpointer lav[2] );
295 static void trw_layer_finish_track ( gpointer lav[2] );
296 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
297 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
298 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
299 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
300 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
301 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
302 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
303 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
304 #ifdef VIK_CONFIG_GEOTAG
305 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
306 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
307 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
308 static void trw_layer_geotagging ( gpointer lav[2] );
310 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
311 static void trw_layer_acquire_routing_cb ( gpointer lav[2] );
312 #ifdef VIK_CONFIG_OPENSTREETMAP
313 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
314 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
316 #ifdef VIK_CONFIG_GEOCACHES
317 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
319 #ifdef VIK_CONFIG_GEOTAG
320 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
322 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
323 static void trw_layer_gps_upload ( gpointer lav[2] );
325 // Specific route versions:
326 // Most track handling functions can handle operating on the route list
327 // However these ones are easier in separate functions
328 static void trw_layer_auto_routes_view ( gpointer lav[2] );
329 static void trw_layer_delete_all_routes ( gpointer lav[2] );
330 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
333 static void trw_layer_properties_item ( gpointer pass_along[7] );
334 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
335 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
336 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
338 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
339 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
340 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
342 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
343 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
344 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
345 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
347 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
348 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
349 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
350 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
351 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
352 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
353 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
354 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
355 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
356 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
357 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
358 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
359 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
360 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
361 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
362 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
363 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
364 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
365 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
366 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
367 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
368 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
369 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
371 static void cached_pixbuf_free ( CachedPixbuf *cp );
372 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
374 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
375 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
377 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
378 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
380 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
381 static void highest_wp_number_reset(VikTrwLayer *vtl);
382 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
383 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
385 // Note for the following tool GtkRadioActionEntry texts:
386 // the very first text value is an internal name not displayed anywhere
387 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
388 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
389 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
390 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
391 static VikToolInterface trw_layer_tools[] = {
392 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
393 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
394 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
396 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
398 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
399 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
400 (VikToolMouseFunc) tool_new_track_click,
401 (VikToolMouseMoveFunc) tool_new_track_move,
402 (VikToolMouseFunc) tool_new_track_release,
403 (VikToolKeyFunc) tool_new_track_key_press,
404 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
405 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
407 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
408 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
409 (VikToolMouseFunc) tool_new_route_click,
410 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
411 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
412 (VikToolKeyFunc) tool_new_track_key_press, // -/#
413 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
414 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
416 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
417 (VikToolConstructorFunc) tool_edit_waypoint_create,
418 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
420 (VikToolMouseFunc) tool_edit_waypoint_click,
421 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
422 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
424 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
426 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
427 (VikToolConstructorFunc) tool_edit_trackpoint_create,
428 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
430 (VikToolMouseFunc) tool_edit_trackpoint_click,
431 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
432 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
434 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
436 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
437 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
438 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
440 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
442 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
443 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
444 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
446 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
450 TOOL_CREATE_WAYPOINT=0,
454 TOOL_EDIT_TRACKPOINT,
460 /****** PARAMETERS ******/
462 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
463 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
465 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
466 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
468 #define MIN_POINT_SIZE 2
469 #define MAX_POINT_SIZE 10
471 #define MIN_ARROW_SIZE 3
472 #define MAX_ARROW_SIZE 20
474 static VikLayerParamScale params_scales[] = {
475 /* min max step digits */
476 { 1, 10, 1, 0 }, /* line_thickness */
477 { 0, 100, 1, 0 }, /* track draw speed factor */
478 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
479 /* 5 * step == how much to turn */
480 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
481 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
482 { 5, 500, 5, 0 }, // 5: image cache_size - " "
483 { 0, 8, 1, 0 }, // 6: Background line thickness
484 { 1, 64, 1, 0 }, /* wpsize */
485 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
486 { 1, 100, 1, 0 }, // 9: elevation factor
487 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
488 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
491 static gchar* params_font_sizes[] = {
492 N_("Extra Extra Small"),
498 N_("Extra Extra Large"),
501 // Needs to align with vik_layer_sort_order_t
502 static gchar* params_sort_order[] = {
504 N_("Name Ascending"),
505 N_("Name Descending"),
509 static VikLayerParamData black_color_default ( void ) {
510 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
512 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
513 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
514 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
515 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
516 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
517 static VikLayerParamData trackbgcolor_default ( void ) {
518 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
520 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
521 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
522 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
524 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
525 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
526 static VikLayerParamData wptextcolor_default ( void ) {
527 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
529 static VikLayerParamData wpbgcolor_default ( void ) {
530 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
532 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
533 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
535 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
536 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
537 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
539 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
541 VikLayerParam trw_layer_params[] = {
542 { 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 },
543 { 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 },
544 { 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 },
546 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
547 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
548 { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
549 { 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 },
550 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default },
551 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
552 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
553 { 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 },
554 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
555 { 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 },
556 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
557 { 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 },
558 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
559 { 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 },
560 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
562 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
563 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 },
564 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
566 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
567 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
568 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
569 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
570 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
572 { 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 },
573 { 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 },
574 { 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 },
575 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
576 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
577 { 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 },
578 { 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 },
579 { 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 },
580 { 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 },
581 { 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 },
583 { 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 },
584 { 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 },
585 { 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 },
586 { 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 },
589 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
591 // Sublayer visibilities
634 *** 1) Add to trw_layer_params and enumeration
635 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
638 /****** END PARAMETERS ******/
640 /* Layer Interface function definitions */
641 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
642 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
643 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
644 static void trw_layer_free ( VikTrwLayer *trwlayer );
645 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
646 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
647 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
648 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
649 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
650 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
651 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
652 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
653 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
654 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
655 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
656 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
657 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
658 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
659 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
660 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
661 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
662 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
663 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
664 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
665 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
666 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
667 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
668 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
669 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
670 /* End Layer Interface function definitions */
672 VikLayerInterface vik_trw_layer_interface = {
679 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
683 params_groups, /* params_groups */
684 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
688 (VikLayerFuncCreate) trw_layer_create,
689 (VikLayerFuncRealize) trw_layer_realize,
690 (VikLayerFuncPostRead) trw_layer_post_read,
691 (VikLayerFuncFree) trw_layer_free,
693 (VikLayerFuncProperties) NULL,
694 (VikLayerFuncDraw) trw_layer_draw,
695 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
697 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
698 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
700 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
701 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
703 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
704 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
705 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
706 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
707 (VikLayerFuncLayerSelected) trw_layer_selected,
709 (VikLayerFuncMarshall) trw_layer_marshall,
710 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
712 (VikLayerFuncSetParam) trw_layer_set_param,
713 (VikLayerFuncGetParam) trw_layer_get_param,
715 (VikLayerFuncReadFileData) a_gpspoint_read_file,
716 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
718 (VikLayerFuncDeleteItem) trw_layer_del_item,
719 (VikLayerFuncCutItem) trw_layer_cut_item,
720 (VikLayerFuncCopyItem) trw_layer_copy_item,
721 (VikLayerFuncPasteItem) trw_layer_paste_item,
722 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
724 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
726 (VikLayerFuncSelectClick) trw_layer_select_click,
727 (VikLayerFuncSelectMove) trw_layer_select_move,
728 (VikLayerFuncSelectRelease) trw_layer_select_release,
729 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
732 GType vik_trw_layer_get_type ()
734 static GType vtl_type = 0;
738 static const GTypeInfo vtl_info =
740 sizeof (VikTrwLayerClass),
741 NULL, /* base_init */
742 NULL, /* base_finalize */
743 NULL, /* class init */
744 NULL, /* class_finalize */
745 NULL, /* class_data */
746 sizeof (VikTrwLayer),
748 NULL /* instance init */
750 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
756 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
758 static gpointer pass_along[6];
764 pass_along[1] = NULL;
765 pass_along[2] = GINT_TO_POINTER (subtype);
766 pass_along[3] = sublayer;
767 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
768 pass_along[5] = NULL;
770 trw_layer_delete_item ( pass_along );
773 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
775 static gpointer pass_along[6];
781 pass_along[1] = NULL;
782 pass_along[2] = GINT_TO_POINTER (subtype);
783 pass_along[3] = sublayer;
784 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
785 pass_along[5] = NULL;
787 trw_layer_copy_item_cb(pass_along);
788 trw_layer_cut_item_cb(pass_along);
791 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
793 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
794 gint subtype = GPOINTER_TO_INT (pass_along[2]);
795 gpointer * sublayer = pass_along[3];
799 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
803 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
804 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
805 if ( wp && wp->name )
808 name = NULL; // Broken :(
810 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
811 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
812 if ( trk && trk->name )
815 name = NULL; // Broken :(
818 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
819 if ( trk && trk->name )
822 name = NULL; // Broken :(
825 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
826 subtype, len, name, data);
830 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
832 trw_layer_copy_item_cb(pass_along);
833 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
834 trw_layer_delete_item(pass_along);
837 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
839 // Slightly cheating method, routing via the panels capability
840 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
843 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
853 GByteArray *ba = g_byte_array_new ();
855 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
856 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
857 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
858 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
860 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
863 g_byte_array_append ( ba, id, il );
871 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
878 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
882 w = vik_waypoint_unmarshall ( item, len );
883 // When copying - we'll create a new name based on the original
884 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
885 vik_trw_layer_add_waypoint ( vtl, name, w );
886 waypoint_convert (NULL, w, &vtl->coord_mode);
889 trw_layer_calculate_bounds_waypoints ( vtl );
891 // Consider if redraw necessary for the new item
892 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
893 vik_layer_emit_update ( VIK_LAYER(vtl) );
896 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
900 t = vik_track_unmarshall ( item, len );
901 // When copying - we'll create a new name based on the original
902 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
903 vik_trw_layer_add_track ( vtl, name, t );
904 vik_track_convert (t, vtl->coord_mode);
907 // Consider if redraw necessary for the new item
908 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
909 vik_layer_emit_update ( VIK_LAYER(vtl) );
912 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
916 t = vik_track_unmarshall ( item, len );
917 // When copying - we'll create a new name based on the original
918 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
919 vik_trw_layer_add_route ( vtl, name, t );
920 vik_track_convert (t, vtl->coord_mode);
923 // Consider if redraw necessary for the new item
924 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
925 vik_layer_emit_update ( VIK_LAYER(vtl) );
931 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
938 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
942 case PARAM_TV: vtl->tracks_visible = data.b; break;
943 case PARAM_WV: vtl->waypoints_visible = data.b; break;
944 case PARAM_RV: vtl->routes_visible = data.b; break;
945 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
946 case PARAM_TLFONTSIZE:
947 if ( data.u < FS_NUM_SIZES ) {
948 vtl->track_font_size = data.u;
949 g_free ( vtl->track_fsize_str );
950 switch ( vtl->track_font_size ) {
951 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
952 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
953 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
954 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
955 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
956 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
957 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
961 case PARAM_DM: vtl->drawmode = data.u; break;
963 vtl->track_color = data.c;
964 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
966 case PARAM_DP: vtl->drawpoints = data.b; break;
968 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
969 vtl->drawpoints_size = data.u;
971 case PARAM_DE: vtl->drawelevation = data.b; break;
972 case PARAM_DS: vtl->drawstops = data.b; break;
973 case PARAM_DL: vtl->drawlines = data.b; break;
974 case PARAM_DD: vtl->drawdirections = data.b; break;
976 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
977 vtl->drawdirections_size = data.u;
979 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
980 vtl->stop_length = data.u;
982 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
983 vtl->elevation_factor = data.u;
985 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
987 vtl->line_thickness = data.u;
988 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
991 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
993 vtl->bg_line_thickness = data.u;
994 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
998 vtl->track_bg_color = data.c;
999 if ( vtl->track_bg_gc )
1000 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1002 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1003 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1004 case PARAM_DLA: vtl->drawlabels = data.b; break;
1005 case PARAM_DI: vtl->drawimages = data.b; break;
1006 case PARAM_IS: if ( data.u != vtl->image_size )
1008 vtl->image_size = data.u;
1009 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1010 g_queue_free ( vtl->image_cache );
1011 vtl->image_cache = g_queue_new ();
1014 case PARAM_IA: vtl->image_alpha = data.u; break;
1015 case PARAM_ICS: vtl->image_cache_size = data.u;
1016 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1017 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1020 vtl->waypoint_color = data.c;
1021 if ( vtl->waypoint_gc )
1022 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1025 vtl->waypoint_text_color = data.c;
1026 if ( vtl->waypoint_text_gc )
1027 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1030 vtl->waypoint_bg_color = data.c;
1031 if ( vtl->waypoint_bg_gc )
1032 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1035 vtl->wpbgand = data.b;
1036 if ( vtl->waypoint_bg_gc )
1037 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1039 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1040 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1041 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1042 case PARAM_WPFONTSIZE:
1043 if ( data.u < FS_NUM_SIZES ) {
1044 vtl->wp_font_size = data.u;
1045 g_free ( vtl->wp_fsize_str );
1046 switch ( vtl->wp_font_size ) {
1047 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1048 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1049 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1050 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1051 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1052 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1053 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1057 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1062 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1064 VikLayerParamData rv;
1067 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1068 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1069 case PARAM_RV: rv.b = vtl->routes_visible; break;
1070 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1071 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1072 case PARAM_DM: rv.u = vtl->drawmode; break;
1073 case PARAM_TC: rv.c = vtl->track_color; break;
1074 case PARAM_DP: rv.b = vtl->drawpoints; break;
1075 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1076 case PARAM_DE: rv.b = vtl->drawelevation; break;
1077 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1078 case PARAM_DS: rv.b = vtl->drawstops; break;
1079 case PARAM_SL: rv.u = vtl->stop_length; break;
1080 case PARAM_DL: rv.b = vtl->drawlines; break;
1081 case PARAM_DD: rv.b = vtl->drawdirections; break;
1082 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1083 case PARAM_LT: rv.u = vtl->line_thickness; break;
1084 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1085 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1086 case PARAM_DI: rv.b = vtl->drawimages; break;
1087 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1088 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1089 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1090 case PARAM_IS: rv.u = vtl->image_size; break;
1091 case PARAM_IA: rv.u = vtl->image_alpha; break;
1092 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1093 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1094 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1095 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1096 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1097 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1098 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1099 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1100 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1101 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1106 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1113 // Use byte arrays to store sublayer data
1114 // much like done elsewhere e.g. vik_layer_marshall_params()
1115 GByteArray *ba = g_byte_array_new ( );
1120 guint object_length;
1123 // the length of the item
1124 // the sublayer type of item
1125 // the the actual item
1126 #define tlm_append(object_pointer, size, type) \
1128 object_length = (size); \
1129 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1130 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1131 g_byte_array_append ( ba, (object_pointer), object_length );
1133 // Layer parameters first
1134 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1135 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1136 g_byte_array_append ( ba, pd, pl );
1139 // Now sublayer data
1140 GHashTableIter iter;
1141 gpointer key, value;
1144 g_hash_table_iter_init ( &iter, vtl->waypoints );
1145 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1146 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1147 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1152 g_hash_table_iter_init ( &iter, vtl->tracks );
1153 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1154 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1155 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1160 g_hash_table_iter_init ( &iter, vtl->routes );
1161 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1162 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1163 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1173 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1175 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1177 gint consumed_length;
1179 // First the overall layer parameters
1180 memcpy(&pl, data, sizeof(pl));
1182 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1185 consumed_length = pl;
1186 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1188 #define tlm_size (*(gint *)data)
1189 // See marshalling above for order of how this is written
1191 data += sizeof_len_and_subtype + tlm_size;
1193 // Now the individual sublayers:
1195 while ( *data && consumed_length < len ) {
1196 // Normally four extra bytes at the end of the datastream
1197 // (since it's a GByteArray and that's where it's length is stored)
1198 // So only attempt read when there's an actual block of sublayer data
1199 if ( consumed_length + tlm_size < len ) {
1201 // Reuse pl to read the subtype from the data stream
1202 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1204 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1205 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1206 gchar *name = g_strdup ( trk->name );
1207 vik_trw_layer_add_track ( vtl, name, trk );
1210 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1211 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1212 gchar *name = g_strdup ( wp->name );
1213 vik_trw_layer_add_waypoint ( vtl, name, wp );
1216 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1217 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1218 gchar *name = g_strdup ( trk->name );
1219 vik_trw_layer_add_route ( vtl, name, trk );
1223 consumed_length += tlm_size + sizeof_len_and_subtype;
1226 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1228 // Not stored anywhere else so need to regenerate
1229 trw_layer_calculate_bounds_waypoints ( vtl );
1234 // Keep interesting hash function at least visible
1236 static guint strcase_hash(gconstpointer v)
1238 // 31 bit hash function
1241 gchar s[128]; // malloc is too slow for reading big files
1244 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1245 p[i] = toupper(t[i]);
1251 for (p += 1; *p != '\0'; p++)
1252 h = (h << 5) - h + *p;
1259 // Stick a 1 at the end of the function name to make it more unique
1260 // thus more easily searchable in a simple text editor
1261 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1263 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1264 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1266 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1267 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1269 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1270 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1271 // and with normal PC processing capabilities - it has negligibile performance impact
1272 // This also minimized the amount of rework - as the management of the hash tables already exists.
1274 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1275 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1276 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1278 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1279 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1280 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1281 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1282 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1283 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1285 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1287 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1289 // Param settings that are not available via the GUI
1290 // Force to on after processing params (which defaults them to off with a zero value)
1291 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1293 rv->draw_sync_done = TRUE;
1294 rv->draw_sync_do = TRUE;
1295 // Everything else is 0, FALSE or NULL
1301 static void trw_layer_free ( VikTrwLayer *trwlayer )
1303 g_hash_table_destroy(trwlayer->waypoints);
1304 g_hash_table_destroy(trwlayer->waypoints_iters);
1305 g_hash_table_destroy(trwlayer->tracks);
1306 g_hash_table_destroy(trwlayer->tracks_iters);
1307 g_hash_table_destroy(trwlayer->routes);
1308 g_hash_table_destroy(trwlayer->routes_iters);
1310 /* ODC: replace with GArray */
1311 trw_layer_free_track_gcs ( trwlayer );
1313 if ( trwlayer->wp_right_click_menu )
1314 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1316 if ( trwlayer->track_right_click_menu )
1317 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1319 if ( trwlayer->tracklabellayout != NULL)
1320 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1322 if ( trwlayer->wplabellayout != NULL)
1323 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1325 if ( trwlayer->waypoint_gc != NULL )
1326 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1328 if ( trwlayer->waypoint_text_gc != NULL )
1329 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1331 if ( trwlayer->waypoint_bg_gc != NULL )
1332 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1334 g_free ( trwlayer->wp_fsize_str );
1335 g_free ( trwlayer->track_fsize_str );
1337 if ( trwlayer->tpwin != NULL )
1338 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1340 if ( trwlayer->tracks_analysis_dialog != NULL )
1341 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1343 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1344 g_queue_free ( trwlayer->image_cache );
1347 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1351 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1352 dp->xmpp = vik_viewport_get_xmpp ( vp );
1353 dp->ympp = vik_viewport_get_ympp ( vp );
1354 dp->width = vik_viewport_get_width ( vp );
1355 dp->height = vik_viewport_get_height ( vp );
1356 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1357 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1359 dp->center = vik_viewport_get_center ( vp );
1360 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1361 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1366 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1367 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1368 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1370 dp->ce1 = dp->center->east_west-w2;
1371 dp->ce2 = dp->center->east_west+w2;
1372 dp->cn1 = dp->center->north_south-h2;
1373 dp->cn2 = dp->center->north_south+h2;
1374 } else if ( dp->lat_lon ) {
1375 VikCoord upperleft, bottomright;
1376 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1377 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1378 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1379 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1380 dp->ce1 = upperleft.east_west;
1381 dp->ce2 = bottomright.east_west;
1382 dp->cn1 = bottomright.north_south;
1383 dp->cn2 = upperleft.north_south;
1386 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1390 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1391 * Here a simple traffic like light colour system is used:
1392 * . slow points are red
1393 * . average is yellow
1394 * . fast points are green
1396 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1399 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1400 if ( average_speed > 0 ) {
1401 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1402 if ( rv < low_speed )
1403 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1404 else if ( rv > high_speed )
1405 return VIK_TRW_LAYER_TRACK_GC_FAST;
1407 return VIK_TRW_LAYER_TRACK_GC_AVER;
1410 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1413 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1415 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1416 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1417 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1418 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1422 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1424 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1426 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1427 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1429 // Fallback if parse failure
1430 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1432 g_free ( label_markup );
1434 gint label_x, label_y;
1436 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1438 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1439 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1443 * distance_in_preferred_units:
1444 * @dist: The source distance in standard SI Units (i.e. metres)
1446 * TODO: This is a generic function that could be moved into globals.c or utils.c
1448 * Probably best used if you have a only few conversions to perform.
1449 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1450 * since it will be doing the preference check on each call
1452 * Returns: The distance in the units as specified by the preferences
1454 static gdouble distance_in_preferred_units ( gdouble dist )
1457 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1458 switch (dist_units) {
1459 case VIK_UNITS_DISTANCE_MILES:
1460 mydist = VIK_METERS_TO_MILES(dist);
1462 // VIK_UNITS_DISTANCE_KILOMETRES:
1464 mydist = dist/1000.0;
1471 * trw_layer_draw_dist_labels:
1473 * Draw a few labels along a track at nicely seperated distances
1474 * This might slow things down if there's many tracks being displayed with this on.
1476 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1478 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1479 25.0, 40.0, 50.0, 75.0, 100.0,
1480 150.0, 200.0, 250.0, 500.0, 1000.0};
1482 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1484 // Convert to specified unit to find the friendly breakdown value
1485 dist = distance_in_preferred_units ( dist );
1489 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1490 if ( chunksd[i] > dist ) {
1492 dist = chunksd[index];
1497 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1499 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1500 gdouble dist_i = dist * i;
1502 // Convert distance back into metres for use in finding a trackpoint
1503 switch (dist_units) {
1504 case VIK_UNITS_DISTANCE_MILES:
1505 dist_i = VIK_MILES_TO_METERS(dist_i);
1507 // VIK_UNITS_DISTANCE_KILOMETRES:
1509 dist_i = dist_i*1000.0;
1513 gdouble dist_current = 0.0;
1514 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1515 gdouble dist_next = 0.0;
1516 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1518 gdouble dist_between_tps = fabs (dist_next - dist_current);
1519 gdouble ratio = 0.0;
1520 // Prevent division by 0 errors
1521 if ( dist_between_tps > 0.0 )
1522 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1524 if ( tp_current && tp_next ) {
1525 // Construct the name based on the distance value
1528 switch (dist_units) {
1529 case VIK_UNITS_DISTANCE_MILES:
1530 units = g_strdup ( _("miles") );
1532 // VIK_UNITS_DISTANCE_KILOMETRES:
1534 units = g_strdup ( _("km") );
1538 // Convert for display
1539 dist_i = distance_in_preferred_units ( dist_i );
1541 // Make the precision of the output related to the unit size.
1543 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1544 else if ( index == 1 )
1545 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1547 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1550 struct LatLon ll_current, ll_next;
1551 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1552 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1554 // positional interpolation
1555 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1556 // but should be good enough over the small scale that I anticipate usage on
1557 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1558 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1560 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1563 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1564 fgcolour = gdk_color_to_string ( &(trk->color) );
1566 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1568 // if highlight mode on, then colour the background in the highlight colour
1570 if ( drawing_highlight )
1571 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1573 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1575 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1577 g_free ( fgcolour );
1578 g_free ( bgcolour );
1585 * trw_layer_draw_track_name_labels:
1587 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1589 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1592 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1593 fgcolour = gdk_color_to_string ( &(trk->color) );
1595 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1597 // if highlight mode on, then colour the background in the highlight colour
1599 if ( drawing_highlight )
1600 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1602 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1604 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1606 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1607 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1608 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1609 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1610 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1611 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1613 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1615 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1618 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1619 // No other labels to draw
1622 GList *ending = g_list_last ( trk->trackpoints );
1623 VikCoord begin_coord = VIK_TRACKPOINT(trk->trackpoints->data)->coord;
1624 VikCoord end_coord = VIK_TRACKPOINT(ending->data)->coord;
1626 gboolean done_start_end = FALSE;
1628 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1629 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1631 // This number can be configured via the settings if you really want to change it
1632 gdouble distance_diff;
1633 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1634 distance_diff = 100.0; // Metres
1636 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1637 // Start and end 'close' together so only draw one label at an average location
1638 gint x1, x2, y1, y2;
1639 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1640 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1642 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1644 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1645 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1648 done_start_end = TRUE;
1652 if ( ! done_start_end ) {
1653 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1654 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1655 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1656 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1657 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1658 g_free ( name_start );
1660 // Don't draw end label if this is the one being created
1661 if ( trk != dp->vtl->current_track ) {
1662 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1663 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1664 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1665 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1666 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1667 g_free ( name_end );
1672 g_free ( fgcolour );
1673 g_free ( bgcolour );
1677 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1679 if ( ! track->visible )
1682 /* TODO: this function is a mess, get rid of any redundancy */
1683 GList *list = track->trackpoints;
1685 gboolean useoldvals = TRUE;
1687 gboolean drawpoints;
1689 gboolean drawelevation;
1690 gdouble min_alt, max_alt, alt_diff = 0;
1692 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1693 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1696 if ( dp->vtl->drawelevation )
1698 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1699 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1700 alt_diff = max_alt - min_alt;
1703 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1704 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1705 trw_layer_draw_track ( id, track, dp, TRUE );
1707 if ( draw_track_outline )
1708 drawpoints = drawstops = FALSE;
1710 drawpoints = dp->vtl->drawpoints;
1711 drawstops = dp->vtl->drawstops;
1714 gboolean drawing_highlight = FALSE;
1715 /* Current track - used for creation */
1716 if ( track == dp->vtl->current_track )
1717 main_gc = dp->vtl->current_track_gc;
1719 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1720 /* Draw all tracks of the layer in special colour */
1721 /* if track is member of selected layer or is the current selected track
1722 then draw in the highlight colour.
1723 NB this supercedes the drawmode */
1724 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1725 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1726 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1727 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1728 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1729 drawing_highlight = TRUE;
1732 if ( !drawing_highlight ) {
1733 // Still need to figure out the gc according to the drawing mode:
1734 switch ( dp->vtl->drawmode ) {
1735 case DRAWMODE_BY_TRACK:
1736 if ( dp->vtl->track_1color_gc )
1737 g_object_unref ( dp->vtl->track_1color_gc );
1738 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1739 main_gc = dp->vtl->track_1color_gc;
1742 // Mostly for DRAWMODE_ALL_SAME_COLOR
1743 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1744 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1751 int x, y, oldx, oldy;
1752 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1754 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1756 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1758 // Draw the first point as something a bit different from the normal points
1759 // ATM it's slightly bigger and a triangle
1761 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1762 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1768 gdouble average_speed = 0.0;
1769 gdouble low_speed = 0.0;
1770 gdouble high_speed = 0.0;
1771 // If necessary calculate these values - which is done only once per track redraw
1772 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1773 // the percentage factor away from the average speed determines transistions between the levels
1774 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1775 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1776 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1779 while ((list = g_list_next(list)))
1781 tp = VIK_TRACKPOINT(list->data);
1782 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1784 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1785 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1786 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1787 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1788 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1790 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1793 * If points are the same in display coordinates, don't draw.
1795 if ( useoldvals && x == oldx && y == oldy )
1797 // Still need to process points to ensure 'stops' are drawn if required
1798 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1799 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1800 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 );
1805 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1806 if ( drawpoints || dp->vtl->drawlines ) {
1807 // setup main_gc for both point and line drawing
1808 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1809 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 ) );
1813 if ( drawpoints && ! draw_track_outline )
1818 * The concept of drawing stops is that a trackpoint
1819 * that is if the next trackpoint has a timestamp far into
1820 * the future, we draw a circle of 6x trackpoint size,
1821 * instead of a rectangle of 2x trackpoint size.
1822 * This is drawn first so the trackpoint will be drawn on top
1825 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1826 /* Stop point. Draw 6x circle. Always in redish colour */
1827 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 );
1829 /* Regular point - draw 2x square. */
1830 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1833 /* Final point - draw 4x circle. */
1834 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 );
1837 if ((!tp->newsegment) && (dp->vtl->drawlines))
1840 /* UTM only: zone check */
1841 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1842 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1845 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1847 if ( draw_track_outline ) {
1848 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1852 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1854 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1856 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1861 tmp[1].y = oldy-FIXALTITUDE(list->data);
1863 tmp[2].y = y-FIXALTITUDE(list->next->data);
1868 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1869 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1871 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1872 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1874 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1879 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1880 // Draw an arrow at the mid point to show the direction of the track
1881 // Code is a rework from vikwindow::draw_ruler()
1882 gint midx = (oldx + x) / 2;
1883 gint midy = (oldy + y) / 2;
1885 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1886 // Avoid divide by zero and ensure at least 1 pixel big
1888 gdouble dx = (oldx - midx) / len;
1889 gdouble dy = (oldy - midy) / len;
1890 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1891 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1901 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1903 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1904 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1906 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1908 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1909 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
1913 * If points are the same in display coordinates, don't draw.
1915 if ( x != oldx || y != oldy )
1917 if ( draw_track_outline )
1918 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1920 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1926 * If points are the same in display coordinates, don't draw.
1928 if ( x != oldx && y != oldy )
1930 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1931 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1939 // Labels drawn after the trackpoints, so the labels are on top
1940 if ( dp->vtl->track_draw_labels ) {
1941 if ( track->max_number_dist_labels > 0 ) {
1942 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
1945 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
1946 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
1952 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1954 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1955 trw_layer_draw_track ( id, track, dp, FALSE );
1959 static void cached_pixbuf_free ( CachedPixbuf *cp )
1961 g_object_unref ( G_OBJECT(cp->pixbuf) );
1962 g_free ( cp->image );
1965 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1967 return strcmp ( cp->image, name );
1970 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1973 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1974 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1975 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1978 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1980 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1982 if ( wp->image && dp->vtl->drawimages )
1984 GdkPixbuf *pixbuf = NULL;
1987 if ( dp->vtl->image_alpha == 0)
1990 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1992 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1995 gchar *image = wp->image;
1996 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1997 if ( ! regularthumb )
1999 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2000 image = "\x12\x00"; /* this shouldn't occur naturally. */
2004 CachedPixbuf *cp = NULL;
2005 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2006 if ( dp->vtl->image_size == 128 )
2007 cp->pixbuf = regularthumb;
2010 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2011 g_assert ( cp->pixbuf );
2012 g_object_unref ( G_OBJECT(regularthumb) );
2014 cp->image = g_strdup ( image );
2016 /* needed so 'click picture' tool knows how big the pic is; we don't
2017 * store it in cp because they may have been freed already. */
2018 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2019 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2021 g_queue_push_head ( dp->vtl->image_cache, cp );
2022 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2023 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2025 pixbuf = cp->pixbuf;
2029 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2035 w = gdk_pixbuf_get_width ( pixbuf );
2036 h = gdk_pixbuf_get_height ( pixbuf );
2038 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2040 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2041 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2042 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2043 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2044 // Highlighted - so draw a little border around the chosen one
2045 // single line seems a little weak so draw 2 of them
2046 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2047 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2048 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2049 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2052 if ( dp->vtl->image_alpha == 255 )
2053 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2055 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2057 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2061 // Draw appropriate symbol - either symbol image or simple types
2062 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2063 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 );
2065 else if ( wp == dp->vtl->current_wp ) {
2066 switch ( dp->vtl->wp_symbol ) {
2067 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;
2068 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;
2069 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;
2070 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 );
2071 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 switch ( dp->vtl->wp_symbol ) {
2076 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;
2077 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;
2078 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;
2079 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 );
2080 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;
2084 if ( dp->vtl->drawlabels )
2086 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2087 gint label_x, label_y;
2089 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2091 // Could this stored in the waypoint rather than recreating each pass?
2092 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2094 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2095 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2097 // Fallback if parse failure
2098 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2100 g_free ( wp_label_markup );
2102 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2103 label_x = x - width/2;
2104 if ( wp->symbol_pixbuf )
2105 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2107 label_y = y - dp->vtl->wp_size - height - 2;
2109 /* if highlight mode on, then draw background text in highlight colour */
2110 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2111 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2112 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2113 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2114 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2116 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2119 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2121 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2126 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2128 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2129 trw_layer_draw_waypoint ( id, wp, dp );
2133 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2135 static struct DrawingParams dp;
2136 g_assert ( l != NULL );
2138 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2140 if ( l->tracks_visible )
2141 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2143 if ( l->routes_visible )
2144 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2146 if (l->waypoints_visible)
2147 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2150 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2153 if ( vtl->track_bg_gc )
2155 g_object_unref ( vtl->track_bg_gc );
2156 vtl->track_bg_gc = NULL;
2158 if ( vtl->track_1color_gc )
2160 g_object_unref ( vtl->track_1color_gc );
2161 vtl->track_1color_gc = NULL;
2163 if ( vtl->current_track_gc )
2165 g_object_unref ( vtl->current_track_gc );
2166 vtl->current_track_gc = NULL;
2168 if ( vtl->current_track_newpoint_gc )
2170 g_object_unref ( vtl->current_track_newpoint_gc );
2171 vtl->current_track_newpoint_gc = NULL;
2174 if ( ! vtl->track_gc )
2176 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2177 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2178 g_array_free ( vtl->track_gc, TRUE );
2179 vtl->track_gc = NULL;
2182 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2184 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2185 gint width = vtl->line_thickness;
2187 if ( vtl->track_gc )
2188 trw_layer_free_track_gcs ( vtl );
2190 if ( vtl->track_bg_gc )
2191 g_object_unref ( vtl->track_bg_gc );
2192 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2194 // Ensure new track drawing heeds line thickness setting
2195 // however always have a minium of 2, as 1 pixel is really narrow
2196 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2198 if ( vtl->current_track_gc )
2199 g_object_unref ( vtl->current_track_gc );
2200 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2201 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2203 // 'newpoint' gc is exactly the same as the current track gc
2204 if ( vtl->current_track_newpoint_gc )
2205 g_object_unref ( vtl->current_track_newpoint_gc );
2206 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2207 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2209 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2211 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2212 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2214 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2215 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2216 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2218 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2220 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2223 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2225 VikTrwLayer *rv = trw_layer_new1 ( vp );
2226 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2228 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2229 /* early exit, as the rest is GUI related */
2233 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2234 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2236 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2237 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2239 trw_layer_new_track_gcs ( rv, vp );
2241 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2242 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2243 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2244 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2246 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2248 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2253 #define SMALL_ICON_SIZE 18
2255 * Can accept a null symbol, and may return null value
2257 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2259 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2260 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2261 // So needing a small icon for the treeview may need some resizing:
2262 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2263 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2267 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2269 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2271 GdkPixbuf *pixbuf = NULL;
2273 if ( track->has_color ) {
2274 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2275 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2276 // Here is some magic found to do the conversion
2277 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2278 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2279 ((track->color.green & 0xff00) << 8) |
2280 (track->color.blue & 0xff00);
2282 gdk_pixbuf_fill ( pixbuf, pixel );
2285 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 );
2288 g_object_unref (pixbuf);
2290 *new_iter = *((GtkTreeIter *) pass_along[1]);
2291 if ( track->is_route )
2292 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2294 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2296 if ( ! track->visible )
2297 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2300 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2302 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2304 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 );
2306 *new_iter = *((GtkTreeIter *) pass_along[1]);
2307 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2309 if ( ! wp->visible )
2310 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2313 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2315 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2318 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2320 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2323 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2325 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2328 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2331 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2333 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2334 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2336 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2338 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2341 if ( g_hash_table_size (vtl->routes) > 0 ) {
2342 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2344 pass_along[0] = &(vtl->routes_iter);
2345 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2347 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2349 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2352 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2353 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2355 pass_along[0] = &(vtl->waypoints_iter);
2356 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2358 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2360 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2365 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2369 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2370 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2371 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2372 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2374 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2376 return (t->visible ^= 1);
2380 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2382 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2384 return (t->visible ^= 1);
2388 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2390 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2392 return (t->visible ^= 1);
2401 * Return a property about tracks for this layer
2403 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2405 return vtl->line_thickness;
2408 // Structure to hold multiple track information for a layer
2417 * Build up layer multiple track information via updating the tooltip_tracks structure
2419 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2421 tt->length = tt->length + vik_track_get_length (tr);
2423 // Ensure times are available
2424 if ( tr->trackpoints &&
2425 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2426 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2429 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2430 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2432 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2433 // Hence initialize to the first 'proper' value
2434 if ( tt->start_time == 0 )
2435 tt->start_time = t1;
2436 if ( tt->end_time == 0 )
2439 // Update find the earliest / last times
2440 if ( t1 < tt->start_time )
2441 tt->start_time = t1;
2442 if ( t2 > tt->end_time )
2445 // Keep track of total time
2446 // there maybe gaps within a track (eg segments)
2447 // but this should be generally good enough for a simple indicator
2448 tt->duration = tt->duration + (int)(t2-t1);
2453 * Generate tooltip text for the layer.
2454 * This is relatively complicated as it considers information for
2455 * no tracks, a single track or multiple tracks
2456 * (which may or may not have timing information)
2458 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2469 static gchar tmp_buf[128];
2472 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2474 // Safety check - I think these should always be valid
2475 if ( vtl->tracks && vtl->waypoints ) {
2476 tooltip_tracks tt = { 0.0, 0, 0 };
2477 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2479 GDate* gdate_start = g_date_new ();
2480 g_date_set_time_t (gdate_start, tt.start_time);
2482 GDate* gdate_end = g_date_new ();
2483 g_date_set_time_t (gdate_end, tt.end_time);
2485 if ( g_date_compare (gdate_start, gdate_end) ) {
2486 // Dates differ so print range on separate line
2487 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2488 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2489 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2492 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2493 if ( tt.start_time != 0 )
2494 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2498 if ( tt.length > 0.0 ) {
2499 gdouble len_in_units;
2501 // Setup info dependent on distance units
2502 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2503 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2504 len_in_units = VIK_METERS_TO_MILES(tt.length);
2507 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2508 len_in_units = tt.length/1000.0;
2511 // Timing information if available
2513 if ( tt.duration > 0 ) {
2514 g_snprintf (tbuf1, sizeof(tbuf1),
2515 _(" in %d:%02d hrs:mins"),
2516 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2518 g_snprintf (tbuf2, sizeof(tbuf2),
2519 _("\n%sTotal Length %.1f %s%s"),
2520 tbuf3, len_in_units, tbuf4, tbuf1);
2523 // Put together all the elements to form compact tooltip text
2524 g_snprintf (tmp_buf, sizeof(tmp_buf),
2525 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2526 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2528 g_date_free (gdate_start);
2529 g_date_free (gdate_end);
2536 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2540 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2542 // Very simple tooltip - may expand detail in the future...
2543 static gchar tmp_buf[32];
2544 g_snprintf (tmp_buf, sizeof(tmp_buf),
2546 g_hash_table_size (l->tracks));
2550 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2552 // Very simple tooltip - may expand detail in the future...
2553 static gchar tmp_buf[32];
2554 g_snprintf (tmp_buf, sizeof(tmp_buf),
2556 g_hash_table_size (l->routes));
2561 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2562 // Same tooltip for a route
2563 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2566 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2567 tr = g_hash_table_lookup ( l->tracks, sublayer );
2569 tr = g_hash_table_lookup ( l->routes, sublayer );
2572 // Could be a better way of handling strings - but this works...
2573 gchar time_buf1[20];
2574 gchar time_buf2[20];
2575 time_buf1[0] = '\0';
2576 time_buf2[0] = '\0';
2577 static gchar tmp_buf[100];
2578 // Compact info: Short date eg (11/20/99), duration and length
2579 // Hopefully these are the things that are most useful and so promoted into the tooltip
2580 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2581 // %x The preferred date representation for the current locale without the time.
2582 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2583 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2584 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2586 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2589 // Get length and consider the appropriate distance units
2590 gdouble tr_len = vik_track_get_length(tr);
2591 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2592 switch (dist_units) {
2593 case VIK_UNITS_DISTANCE_KILOMETRES:
2594 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2596 case VIK_UNITS_DISTANCE_MILES:
2597 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2606 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2608 // Very simple tooltip - may expand detail in the future...
2609 static gchar tmp_buf[32];
2610 g_snprintf (tmp_buf, sizeof(tmp_buf),
2612 g_hash_table_size (l->waypoints));
2616 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2618 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2619 // NB It's OK to return NULL
2624 return w->description;
2634 * Function to show basic track point information on the statusbar
2636 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2639 switch (a_vik_get_units_height ()) {
2640 case VIK_UNITS_HEIGHT_FEET:
2641 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2644 //VIK_UNITS_HEIGHT_METRES:
2645 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2650 if ( trkpt->has_timestamp ) {
2651 // Compact date time format
2652 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2656 // Position is put later on, as this bit may not be seen if the display is not big enough,
2657 // one can easily use the current pointer position to see this if needed
2658 gchar *lat = NULL, *lon = NULL;
2659 static struct LatLon ll;
2660 vik_coord_to_latlon (&(trkpt->coord), &ll);
2661 a_coords_latlon_to_string ( &ll, &lat, &lon );
2664 // Again is put later on, as this bit may not be seen if the display is not big enough
2665 // trackname can be seen from the treeview (when enabled)
2666 // Also name could be very long to not leave room for anything else
2669 if ( vtl->current_tp_track ) {
2670 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2673 // Combine parts to make overall message
2674 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2675 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2682 * Function to show basic waypoint information on the statusbar
2684 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2687 switch (a_vik_get_units_height ()) {
2688 case VIK_UNITS_HEIGHT_FEET:
2689 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2692 //VIK_UNITS_HEIGHT_METRES:
2693 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2697 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2698 // one can easily use the current pointer position to see this if needed
2699 gchar *lat = NULL, *lon = NULL;
2700 static struct LatLon ll;
2701 vik_coord_to_latlon (&(wpt->coord), &ll);
2702 a_coords_latlon_to_string ( &ll, &lat, &lon );
2704 // Combine parts to make overall message
2707 // Add comment if available
2708 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2710 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2711 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2718 * General layer selection function, find out which bit is selected and take appropriate action
2720 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2723 l->current_wp = NULL;
2724 l->current_wp_id = NULL;
2725 trw_layer_cancel_current_tp ( l, FALSE );
2728 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2732 case VIK_TREEVIEW_TYPE_LAYER:
2734 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2735 /* Mark for redraw */
2740 case VIK_TREEVIEW_TYPE_SUBLAYER:
2744 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2746 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2747 /* Mark for redraw */
2751 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2753 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2754 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2755 /* Mark for redraw */
2759 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2761 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2762 /* Mark for redraw */
2766 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2768 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2769 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2770 /* Mark for redraw */
2774 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2776 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2777 /* Mark for redraw */
2781 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2783 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2785 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2786 // Show some waypoint info
2787 set_statusbar_msg_info_wpt ( l, wpt );
2788 /* Mark for redraw */
2795 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2804 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2809 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2814 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2819 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2821 return l->waypoints;
2824 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2826 return ! ( g_hash_table_size ( vtl->tracks ) ||
2827 g_hash_table_size ( vtl->routes ) ||
2828 g_hash_table_size ( vtl->waypoints ) );
2831 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2833 return vtl->tracks_visible;
2836 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2838 return vtl->routes_visible;
2841 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2843 return vtl->waypoints_visible;
2847 * ATM use a case sensitive find
2848 * Finds the first one
2850 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2852 if ( wp && wp->name )
2853 if ( ! strcmp ( wp->name, name ) )
2859 * Get waypoint by name - not guaranteed to be unique
2860 * Finds the first one
2862 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2864 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2868 * ATM use a case sensitive find
2869 * Finds the first one
2871 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2873 if ( trk && trk->name )
2874 if ( ! strcmp ( trk->name, name ) )
2880 * Get track by name - not guaranteed to be unique
2881 * Finds the first one
2883 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2885 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2889 * Get route by name - not guaranteed to be unique
2890 * Finds the first one
2892 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2894 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2897 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2899 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2900 maxmin[0].lat = trk->bbox.north;
2901 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2902 maxmin[1].lat = trk->bbox.south;
2903 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2904 maxmin[0].lon = trk->bbox.east;
2905 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2906 maxmin[1].lon = trk->bbox.west;
2909 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2911 // Continually reuse maxmin to find the latest maximum and minimum values
2912 // First set to waypoints bounds
2913 maxmin[0].lat = vtl->waypoints_bbox.north;
2914 maxmin[1].lat = vtl->waypoints_bbox.south;
2915 maxmin[0].lon = vtl->waypoints_bbox.east;
2916 maxmin[1].lon = vtl->waypoints_bbox.west;
2917 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2918 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2921 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2923 /* 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... */
2924 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2925 trw_layer_find_maxmin (vtl, maxmin);
2926 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2930 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2931 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2936 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2939 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2940 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2942 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2945 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2947 /* First set the center [in case previously viewing from elsewhere] */
2948 /* Then loop through zoom levels until provided positions are in view */
2949 /* This method is not particularly fast - but should work well enough */
2950 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2952 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2953 vik_viewport_set_center_coord ( vvp, &coord );
2955 /* Convert into definite 'smallest' and 'largest' positions */
2956 struct LatLon minmin;
2957 if ( maxmin[0].lat < maxmin[1].lat )
2958 minmin.lat = maxmin[0].lat;
2960 minmin.lat = maxmin[1].lat;
2962 struct LatLon maxmax;
2963 if ( maxmin[0].lon > maxmin[1].lon )
2964 maxmax.lon = maxmin[0].lon;
2966 maxmax.lon = maxmin[1].lon;
2968 /* Never zoom in too far - generally not that useful, as too close ! */
2969 /* Always recalculate the 'best' zoom level */
2971 vik_viewport_set_zoom ( vvp, zoom );
2973 gdouble min_lat, max_lat, min_lon, max_lon;
2974 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2975 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2976 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2977 /* NB I think the logic used in this test to determine if the bounds is within view
2978 fails if track goes across 180 degrees longitude.
2979 Hopefully that situation is not too common...
2980 Mind you viking doesn't really do edge locations to well anyway */
2981 if ( min_lat < minmin.lat &&
2982 max_lat > minmin.lat &&
2983 min_lon < maxmax.lon &&
2984 max_lon > maxmax.lon )
2985 /* Found within zoom level */
2990 vik_viewport_set_zoom ( vvp, zoom );
2994 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2996 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2997 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2998 trw_layer_find_maxmin (vtl, maxmin);
2999 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3002 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3007 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
3009 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])) ) ) {
3010 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3013 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
3016 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3018 GtkWidget *file_selector;
3020 gboolean failed = FALSE;
3021 file_selector = gtk_file_chooser_dialog_new (title,
3023 GTK_FILE_CHOOSER_ACTION_SAVE,
3024 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3025 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3027 gchar *cwd = g_get_current_dir();
3029 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3033 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3035 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3037 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3038 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
3040 gtk_widget_hide ( file_selector );
3041 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3042 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3043 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3048 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3050 gtk_widget_hide ( file_selector );
3051 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3052 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3053 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3058 gtk_widget_destroy ( file_selector );
3060 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
3063 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
3065 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
3068 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
3070 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
3073 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
3075 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3076 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3077 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3078 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3080 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3082 g_free ( auto_save_name );
3085 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
3087 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3088 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3089 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3090 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3092 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3094 g_free ( auto_save_name );
3098 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3101 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
3103 gchar *name_used = NULL;
3106 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3107 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3108 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
3109 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3111 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
3115 gchar *quoted_file = g_shell_quote ( name_used );
3116 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3117 g_free ( quoted_file );
3118 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3120 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
3121 g_error_free ( err );
3125 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3126 //g_remove ( name_used );
3127 // Perhaps should be deleted when the program ends?
3128 // For now leave it to the user to delete it / use system temp cleanup methods.
3129 g_free ( name_used );
3133 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
3135 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
3138 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
3140 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
3143 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
3145 gpointer layer_and_vlp[2];
3146 layer_and_vlp[0] = pass_along[0];
3147 layer_and_vlp[1] = pass_along[1];
3149 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3151 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3152 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3154 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3156 if ( !trk || !trk->name )
3159 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3160 gchar *auto_save_name = g_strdup ( trk->name );
3161 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3162 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3164 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
3166 g_free ( auto_save_name );
3170 VikWaypoint *wp; // input
3171 gpointer uuid; // output
3174 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3176 wpu_udata *user_data = udata;
3177 if ( wp == user_data->wp ) {
3178 user_data->uuid = id;
3184 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
3186 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3187 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
3188 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3190 GTK_RESPONSE_REJECT,
3192 GTK_RESPONSE_ACCEPT,
3195 GtkWidget *label, *entry;
3196 label = gtk_label_new(_("Waypoint Name:"));
3197 entry = gtk_entry_new();
3199 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3200 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3201 gtk_widget_show_all ( label );
3202 gtk_widget_show_all ( entry );
3204 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3206 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3208 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3209 // Find *first* wp with the given name
3210 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
3213 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
3216 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
3217 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3219 // Find and select on the side panel
3224 // Hmmm, want key of it
3225 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3227 if ( wpf && udata.uuid ) {
3228 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
3229 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
3238 gtk_widget_destroy ( dia );
3241 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3243 gchar *default_name = highest_wp_number_get(vtl);
3244 VikWaypoint *wp = vik_waypoint_new();
3245 gchar *returned_name;
3247 wp->coord = *def_coord;
3249 // Attempt to auto set height if DEM data is available
3250 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
3251 if ( elev != VIK_DEM_INVALID_ELEVATION )
3252 wp->altitude = (gdouble)elev;
3254 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3256 if ( returned_name )
3259 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3260 g_free (default_name);
3261 g_free (returned_name);
3264 g_free (default_name);
3265 vik_waypoint_free(wp);
3269 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
3271 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3272 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3273 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3274 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3275 VikViewport *vvp = vik_window_viewport(vw);
3277 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3278 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3279 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3280 trw_layer_calculate_bounds_waypoints ( vtl );
3281 vik_layers_panel_emit_update ( vlp );
3284 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
3286 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3287 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3288 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3290 trw_layer_find_maxmin (vtl, maxmin);
3291 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3292 trw_layer_calculate_bounds_waypoints ( vtl );
3293 vik_layers_panel_emit_update ( vlp );
3296 #ifdef VIK_CONFIG_GEOTAG
3297 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
3299 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3301 // Update directly - not changing the mtime
3302 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3305 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3307 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3310 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3314 * Use code in separate file for this feature as reasonably complex
3316 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3318 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3319 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3320 // Unset so can be reverified later if necessary
3321 vtl->has_verified_thumbnails = FALSE;
3323 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3329 static void trw_layer_geotagging ( gpointer lav[2] )
3331 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3332 // Unset so can be reverified later if necessary
3333 vtl->has_verified_thumbnails = FALSE;
3335 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3342 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3345 * Acquire into this TRW Layer straight from GPS Device
3347 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3349 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3350 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3351 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3352 VikViewport *vvp = vik_window_viewport(vw);
3354 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3355 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3359 * Acquire into this TRW Layer from Directions
3361 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3363 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3364 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3365 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3366 VikViewport *vvp = vik_window_viewport(vw);
3368 a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3371 #ifdef VIK_CONFIG_OPENSTREETMAP
3373 * Acquire into this TRW Layer from OSM
3375 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3377 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3378 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3379 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3380 VikViewport *vvp = vik_window_viewport(vw);
3382 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3386 * Acquire into this TRW Layer from OSM for 'My' Traces
3388 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3390 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3391 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3392 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3393 VikViewport *vvp = vik_window_viewport(vw);
3395 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3399 #ifdef VIK_CONFIG_GEOCACHES
3401 * Acquire into this TRW Layer from Geocaching.com
3403 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3405 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3406 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3407 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3408 VikViewport *vvp = vik_window_viewport(vw);
3410 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3414 #ifdef VIK_CONFIG_GEOTAG
3416 * Acquire into this TRW Layer from images
3418 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3420 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3421 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3422 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3423 VikViewport *vvp = vik_window_viewport(vw);
3425 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3426 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3428 // Reverify thumbnails as they may have changed
3429 vtl->has_verified_thumbnails = FALSE;
3430 trw_layer_verify_thumbnails ( vtl, NULL );
3434 static void trw_layer_gps_upload ( gpointer lav[2] )
3436 gpointer pass_along[6];
3437 pass_along[0] = lav[0];
3438 pass_along[1] = lav[1];
3439 pass_along[2] = NULL; // No track - operate on the layer
3440 pass_along[3] = NULL;
3441 pass_along[4] = NULL;
3442 pass_along[5] = NULL;
3444 trw_layer_gps_upload_any ( pass_along );
3448 * If pass_along[3] is defined that this will upload just that track
3450 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3452 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3453 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3455 // May not actually get a track here as pass_along[2&3] can be null
3456 VikTrack *track = NULL;
3457 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3458 gboolean xfer_all = FALSE;
3460 if ( pass_along[2] ) {
3462 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3463 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3466 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3467 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3470 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3473 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3477 else if ( !pass_along[4] )
3478 xfer_all = TRUE; // i.e. whole layer
3480 if (track && !track->visible) {
3481 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3485 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3486 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3487 GTK_DIALOG_DESTROY_WITH_PARENT,
3489 GTK_RESPONSE_ACCEPT,
3491 GTK_RESPONSE_REJECT,
3494 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3495 GtkWidget *response_w = NULL;
3496 #if GTK_CHECK_VERSION (2, 20, 0)
3497 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3501 gtk_widget_grab_focus ( response_w );
3503 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3505 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3506 datasource_gps_clean_up ( dgs );
3507 gtk_widget_destroy ( dialog );
3511 // Get info from reused datasource dialog widgets
3512 gchar* protocol = datasource_gps_get_protocol ( dgs );
3513 gchar* port = datasource_gps_get_descriptor ( dgs );
3514 // NB don't free the above strings as they're references to values held elsewhere
3515 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3516 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3517 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3518 gboolean turn_off = datasource_gps_get_off ( dgs );
3520 gtk_widget_destroy ( dialog );
3522 // When called from the viewport - work the corresponding layerspanel:
3524 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3527 // Apply settings to transfer to the GPS device
3534 vik_layers_panel_get_viewport (vlp),
3543 * Acquire into this TRW Layer from any GPS Babel supported file
3545 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3547 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3548 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3549 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3550 VikViewport *vvp = vik_window_viewport(vw);
3552 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3555 static void trw_layer_new_wp ( gpointer lav[2] )
3557 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3558 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3559 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3560 instead return true if you want to update. */
3561 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 ) {
3562 trw_layer_calculate_bounds_waypoints ( vtl );
3563 vik_layers_panel_emit_update ( vlp );
3567 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3569 vtl->current_track = vik_track_new();
3570 vik_track_set_defaults ( vtl->current_track );
3571 vtl->current_track->visible = TRUE;
3572 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3573 // Create track with the preferred colour from the layer properties
3574 vtl->current_track->color = vtl->track_color;
3576 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3577 vtl->current_track->has_color = TRUE;
3578 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3581 static void trw_layer_new_track ( gpointer lav[2] )
3583 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3585 if ( ! vtl->current_track ) {
3586 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3587 new_track_create_common ( vtl, name );
3590 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3594 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3596 vtl->current_track = vik_track_new();
3597 vik_track_set_defaults ( vtl->current_track );
3598 vtl->current_track->visible = TRUE;
3599 vtl->current_track->is_route = TRUE;
3600 // By default make all routes red
3601 vtl->current_track->has_color = TRUE;
3602 gdk_color_parse ( "red", &vtl->current_track->color );
3603 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3606 static void trw_layer_new_route ( gpointer lav[2] )
3608 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3610 if ( ! vtl->current_track ) {
3611 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3612 new_route_create_common ( vtl, name );
3614 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3618 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3620 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3621 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3623 if ( g_hash_table_size (vtl->routes) > 0 ) {
3624 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3625 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3626 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3627 vik_layers_panel_emit_update ( vlp );
3632 static void trw_layer_finish_track ( gpointer lav[2] )
3634 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3635 vtl->current_track = NULL;
3636 vik_layer_emit_update ( VIK_LAYER(vtl) );
3639 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3641 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3642 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3644 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3645 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3646 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3647 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3648 vik_layers_panel_emit_update ( vlp );
3652 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3654 /* NB do not care if wp is visible or not */
3655 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3658 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3660 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3661 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3663 /* Only 1 waypoint - jump straight to it */
3664 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3665 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3666 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3668 /* If at least 2 waypoints - find center and then zoom to fit */
3669 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3671 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3672 maxmin[0].lat = vtl->waypoints_bbox.north;
3673 maxmin[1].lat = vtl->waypoints_bbox.south;
3674 maxmin[0].lon = vtl->waypoints_bbox.east;
3675 maxmin[1].lon = vtl->waypoints_bbox.west;
3676 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3679 vik_layers_panel_emit_update ( vlp );
3682 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3684 static gpointer pass_along[2];
3686 GtkWidget *export_submenu;
3687 pass_along[0] = vtl;
3688 pass_along[1] = vlp;
3690 item = gtk_menu_item_new();
3691 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3692 gtk_widget_show ( item );
3694 if ( vtl->current_track ) {
3695 if ( vtl->current_track->is_route )
3696 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3698 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3700 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3701 gtk_widget_show ( item );
3704 item = gtk_menu_item_new ();
3705 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3706 gtk_widget_show ( item );
3709 /* Now with icons */
3710 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3711 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3712 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3713 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3714 gtk_widget_show ( item );
3716 GtkWidget *view_submenu = gtk_menu_new();
3717 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3718 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3719 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3720 gtk_widget_show ( item );
3721 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3723 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3724 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3725 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3726 gtk_widget_show ( item );
3728 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3729 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3730 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3731 gtk_widget_show ( item );
3733 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3734 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3735 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3736 gtk_widget_show ( item );
3738 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3739 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3741 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3742 gtk_widget_show ( item );
3744 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3746 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3747 gtk_widget_show ( item );
3749 export_submenu = gtk_menu_new ();
3750 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3751 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3752 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3753 gtk_widget_show ( item );
3754 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3756 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), 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 GPS_Mapper...") );
3762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3763 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3764 gtk_widget_show ( item );
3766 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3767 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3768 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3769 gtk_widget_show ( item );
3771 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3772 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3773 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3774 gtk_widget_show ( item );
3776 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3777 item = gtk_menu_item_new_with_mnemonic ( external1 );
3778 g_free ( external1 );
3779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3780 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3781 gtk_widget_show ( item );
3783 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3784 item = gtk_menu_item_new_with_mnemonic ( external2 );
3785 g_free ( external2 );
3786 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3787 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3788 gtk_widget_show ( item );
3790 GtkWidget *new_submenu = gtk_menu_new();
3791 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3792 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3793 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3794 gtk_widget_show(item);
3795 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3797 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3798 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3800 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3801 gtk_widget_show ( item );
3803 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3804 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3805 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3806 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3807 gtk_widget_show ( item );
3808 // Make it available only when a new track *not* already in progress
3809 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3811 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3812 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3814 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3815 gtk_widget_show ( item );
3816 // Make it available only when a new track *not* already in progress
3817 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3819 #ifdef VIK_CONFIG_GEOTAG
3820 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3821 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3822 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3823 gtk_widget_show ( item );
3826 GtkWidget *acquire_submenu = gtk_menu_new ();
3827 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3828 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3829 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3830 gtk_widget_show ( item );
3831 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3833 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3834 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3835 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3836 gtk_widget_show ( item );
3838 /* FIXME: only add menu when at least a routing engine has support for Directions */
3839 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3840 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3841 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3842 gtk_widget_show ( item );
3844 #ifdef VIK_CONFIG_OPENSTREETMAP
3845 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3846 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3847 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3848 gtk_widget_show ( item );
3850 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3851 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3852 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3853 gtk_widget_show ( item );
3856 #ifdef VIK_CONFIG_GEONAMES
3857 GtkWidget *wikipedia_submenu = gtk_menu_new();
3858 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3859 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3860 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3861 gtk_widget_show(item);
3862 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3864 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3865 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3866 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3867 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3868 gtk_widget_show ( item );
3870 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3871 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3873 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3874 gtk_widget_show ( item );
3877 #ifdef VIK_CONFIG_GEOCACHES
3878 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3880 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3881 gtk_widget_show ( item );
3884 #ifdef VIK_CONFIG_GEOTAG
3885 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3886 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3887 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3888 gtk_widget_show ( item );
3891 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3892 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3893 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3894 gtk_widget_show ( item );
3896 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3898 GtkWidget *upload_submenu = gtk_menu_new ();
3899 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3901 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3902 gtk_widget_show ( item );
3903 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3905 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3906 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3908 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3909 gtk_widget_show ( item );
3911 #ifdef VIK_CONFIG_OPENSTREETMAP
3912 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3913 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3914 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3915 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3916 gtk_widget_show ( item );
3919 GtkWidget *delete_submenu = gtk_menu_new ();
3920 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3921 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3922 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3923 gtk_widget_show ( item );
3924 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3926 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3927 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3929 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3930 gtk_widget_show ( item );
3932 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3933 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3934 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3935 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3936 gtk_widget_show ( item );
3938 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3939 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3941 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3942 gtk_widget_show ( item );
3944 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3945 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3947 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3948 gtk_widget_show ( item );
3950 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3951 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3952 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3953 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3954 gtk_widget_show ( item );
3956 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3957 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3958 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3959 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3960 gtk_widget_show ( item );
3962 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3963 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3965 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3966 gtk_widget_show ( item );
3969 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3970 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3972 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3973 gtk_widget_show ( item );
3977 // Fake Waypoint UUIDs vi simple increasing integer
3978 static guint wp_uuid = 0;
3980 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3984 vik_waypoint_set_name (wp, name);
3986 if ( VIK_LAYER(vtl)->realized )
3988 // Do we need to create the sublayer:
3989 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3990 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3993 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3995 // Visibility column always needed for waypoints
3996 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 );
3998 // Actual setting of visibility dependent on the waypoint
3999 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4001 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4003 // Sort now as post_read is not called on a realized waypoint
4004 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4007 highest_wp_number_add_wp(vtl, name);
4008 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4012 // Fake Track UUIDs vi simple increasing integer
4013 static guint tr_uuid = 0;
4015 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4019 vik_track_set_name (t, name);
4021 if ( VIK_LAYER(vtl)->realized )
4023 // Do we need to create the sublayer:
4024 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4025 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4028 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4029 // Visibility column always needed for tracks
4030 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 );
4032 // Actual setting of visibility dependent on the track
4033 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4035 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4037 // Sort now as post_read is not called on a realized track
4038 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4041 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4043 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
4046 // Fake Route UUIDs vi simple increasing integer
4047 static guint rt_uuid = 0;
4049 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4053 vik_track_set_name (t, name);
4055 if ( VIK_LAYER(vtl)->realized )
4057 // Do we need to create the sublayer:
4058 if ( g_hash_table_size (vtl->routes) == 0 ) {
4059 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4062 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4063 // Visibility column always needed for routes
4064 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 );
4065 // Actual setting of visibility dependent on the route
4066 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4068 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4070 // Sort now as post_read is not called on a realized route
4071 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4074 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4076 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
4079 /* to be called whenever a track has been deleted or may have been changed. */
4080 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4082 if (vtl->current_tp_track == trk )
4083 trw_layer_cancel_current_tp ( vtl, FALSE );
4087 * Normally this is done to due the waypoint size preference having changed
4089 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4091 GHashTableIter iter;
4092 gpointer key, value;
4095 g_hash_table_iter_init ( &iter, vtl->waypoints );
4096 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4097 VikWaypoint *wp = VIK_WAYPOINT(value);
4099 // Reapply symbol setting to update the pixbuf
4100 gchar *tmp_symbol = g_strdup ( wp->symbol );
4101 vik_waypoint_set_symbol ( wp, tmp_symbol );
4102 g_free ( tmp_symbol );
4108 * trw_layer_new_unique_sublayer_name:
4110 * Allocates a unique new name
4112 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4115 gchar *newname = g_strdup(name);
4120 switch ( sublayer_type ) {
4121 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4122 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4124 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4125 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4128 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4131 // If found a name already in use try adding 1 to it and we try again
4133 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4135 newname = new_newname;
4138 } while ( id != NULL);
4143 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4145 // No more uniqueness of name forced when loading from a file
4146 // This now makes this function a little redunant as we just flow the parameters through
4147 vik_trw_layer_add_waypoint ( vtl, name, wp );
4150 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4152 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4153 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4154 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4155 vik_track_free ( tr );
4156 vtl->route_finder_append = FALSE; /* this means we have added it */
4159 // No more uniqueness of name forced when loading from a file
4161 vik_trw_layer_add_route ( vtl, name, tr );
4163 vik_trw_layer_add_track ( vtl, name, tr );
4165 if ( vtl->route_finder_check_added_track ) {
4166 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4167 vtl->route_finder_added_track = tr;
4172 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4174 *l = g_list_append(*l, id);
4178 * Move an item from one TRW layer to another TRW layer
4180 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4182 // TODO reconsider strategy when moving within layer (if anything...)
4183 gboolean rename = ( vtl_src != vtl_dest );
4187 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4188 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4192 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4194 newname = g_strdup ( trk->name );
4196 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4197 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4199 vik_trw_layer_delete_track ( vtl_src, trk );
4202 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4203 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4207 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4209 newname = g_strdup ( trk->name );
4211 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4212 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4214 vik_trw_layer_delete_route ( vtl_src, trk );
4217 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4218 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4222 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4224 newname = g_strdup ( wp->name );
4226 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4227 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4229 trw_layer_delete_waypoint ( vtl_src, wp );
4231 // Recalculate bounds even if not renamed as maybe dragged between layers
4232 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4233 trw_layer_calculate_bounds_waypoints ( vtl_src );
4237 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4239 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4240 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4242 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4243 GList *items = NULL;
4246 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4247 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4249 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4250 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4252 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4253 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4258 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4259 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4260 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4261 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4263 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4270 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4271 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4276 VikTrack *trk; // input
4277 gpointer uuid; // output
4280 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4282 trku_udata *user_data = udata;
4283 if ( trk == user_data->trk ) {
4284 user_data->uuid = id;
4290 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4292 gboolean was_visible = FALSE;
4294 if ( trk && trk->name ) {
4296 if ( trk == vtl->current_track ) {
4297 vtl->current_track = NULL;
4298 vtl->current_tp_track = NULL;
4299 vtl->current_tp_id = NULL;
4300 vtl->moving_tp = FALSE;
4303 was_visible = trk->visible;
4305 if ( trk == vtl->route_finder_current_track )
4306 vtl->route_finder_current_track = NULL;
4308 if ( trk == vtl->route_finder_added_track )
4309 vtl->route_finder_added_track = NULL;
4315 // Hmmm, want key of it
4316 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4318 if ( trkf && udata.uuid ) {
4319 /* could be current_tp, so we have to check */
4320 trw_layer_cancel_tps_of_track ( vtl, trk );
4322 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4325 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4326 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4327 g_hash_table_remove ( vtl->tracks, udata.uuid );
4329 // If last sublayer, then remove sublayer container
4330 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4331 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4339 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4341 gboolean was_visible = FALSE;
4343 if ( trk && trk->name ) {
4345 if ( trk == vtl->current_track ) {
4346 vtl->current_track = NULL;
4347 vtl->current_tp_track = NULL;
4348 vtl->current_tp_id = NULL;
4349 vtl->moving_tp = FALSE;
4352 was_visible = trk->visible;
4354 if ( trk == vtl->route_finder_current_track )
4355 vtl->route_finder_current_track = NULL;
4357 if ( trk == vtl->route_finder_added_track )
4358 vtl->route_finder_added_track = NULL;
4364 // Hmmm, want key of it
4365 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4367 if ( trkf && udata.uuid ) {
4368 /* could be current_tp, so we have to check */
4369 trw_layer_cancel_tps_of_track ( vtl, trk );
4371 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4374 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4375 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4376 g_hash_table_remove ( vtl->routes, udata.uuid );
4378 // If last sublayer, then remove sublayer container
4379 if ( g_hash_table_size (vtl->routes) == 0 ) {
4380 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4388 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4390 gboolean was_visible = FALSE;
4392 if ( wp && wp->name ) {
4394 if ( wp == vtl->current_wp ) {
4395 vtl->current_wp = NULL;
4396 vtl->current_wp_id = NULL;
4397 vtl->moving_wp = FALSE;
4400 was_visible = wp->visible;
4406 // Hmmm, want key of it
4407 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4409 if ( wpf && udata.uuid ) {
4410 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4413 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4414 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4416 highest_wp_number_remove_wp(vtl, wp->name);
4417 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4419 // If last sublayer, then remove sublayer container
4420 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4421 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4431 // Only for temporary use by trw_layer_delete_waypoint_by_name
4432 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4434 wpu_udata *user_data = udata;
4435 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4436 user_data->uuid = id;
4443 * Delete a waypoint by the given name
4444 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4445 * as there be multiple waypoints with the same name
4447 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4450 // Fake a waypoint with the given name
4451 udata.wp = vik_waypoint_new ();
4452 vik_waypoint_set_name (udata.wp, name);
4453 // Currently only the name is used in this waypoint find function
4456 // Hmmm, want key of it
4457 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4459 vik_waypoint_free (udata.wp);
4461 if ( wpf && udata.uuid )
4462 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4468 VikTrack *trk; // input
4469 gpointer uuid; // output
4472 // Only for temporary use by trw_layer_delete_track_by_name
4473 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4475 tpu_udata *user_data = udata;
4476 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4477 user_data->uuid = id;
4484 * Delete a track by the given name
4485 * NOTE: ATM this will delete the first encountered Track with the specified name
4486 * as there may be multiple tracks with the same name within the specified hash table
4488 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4491 // Fake a track with the given name
4492 udata.trk = vik_track_new ();
4493 vik_track_set_name (udata.trk, name);
4494 // Currently only the name is used in this waypoint find function
4497 // Hmmm, want key of it
4498 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4500 vik_track_free (udata.trk);
4502 if ( trkf && udata.uuid ) {
4503 // This could be a little better written...
4504 if ( vtl->tracks == ht_tracks )
4505 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4506 if ( vtl->routes == ht_tracks )
4507 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4514 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4516 vik_treeview_item_delete (vt, it );
4519 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4522 vtl->current_track = NULL;
4523 vtl->route_finder_current_track = NULL;
4524 vtl->route_finder_added_track = NULL;
4525 if (vtl->current_tp_track)
4526 trw_layer_cancel_current_tp(vtl, FALSE);
4528 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4529 g_hash_table_remove_all(vtl->routes_iters);
4530 g_hash_table_remove_all(vtl->routes);
4532 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4534 vik_layer_emit_update ( VIK_LAYER(vtl) );
4537 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4540 vtl->current_track = NULL;
4541 vtl->route_finder_current_track = NULL;
4542 vtl->route_finder_added_track = NULL;
4543 if (vtl->current_tp_track)
4544 trw_layer_cancel_current_tp(vtl, FALSE);
4546 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4547 g_hash_table_remove_all(vtl->tracks_iters);
4548 g_hash_table_remove_all(vtl->tracks);
4550 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4552 vik_layer_emit_update ( VIK_LAYER(vtl) );
4555 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4557 vtl->current_wp = NULL;
4558 vtl->current_wp_id = NULL;
4559 vtl->moving_wp = FALSE;
4561 highest_wp_number_reset(vtl);
4563 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4564 g_hash_table_remove_all(vtl->waypoints_iters);
4565 g_hash_table_remove_all(vtl->waypoints);
4567 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4569 vik_layer_emit_update ( VIK_LAYER(vtl) );
4572 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4574 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4575 // Get confirmation from the user
4576 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4577 _("Are you sure you want to delete all tracks in %s?"),
4578 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4579 vik_trw_layer_delete_all_tracks (vtl);
4582 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4584 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4585 // Get confirmation from the user
4586 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4587 _("Are you sure you want to delete all routes in %s?"),
4588 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4589 vik_trw_layer_delete_all_routes (vtl);
4592 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4594 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4595 // Get confirmation from the user
4596 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4597 _("Are you sure you want to delete all waypoints in %s?"),
4598 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4599 vik_trw_layer_delete_all_waypoints (vtl);
4602 static void trw_layer_delete_item ( gpointer pass_along[6] )
4604 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4605 gboolean was_visible = FALSE;
4606 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4608 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4609 if ( wp && wp->name ) {
4610 if ( GPOINTER_TO_INT ( pass_along[4]) )
4611 // Get confirmation from the user
4612 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4613 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4614 _("Are you sure you want to delete the waypoint \"%s\""),
4617 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4620 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4622 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4623 if ( trk && trk->name ) {
4624 if ( GPOINTER_TO_INT ( pass_along[4]) )
4625 // Get confirmation from the user
4626 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4627 _("Are you sure you want to delete the track \"%s\""),
4630 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4635 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4636 if ( trk && trk->name ) {
4637 if ( GPOINTER_TO_INT ( pass_along[4]) )
4638 // Get confirmation from the user
4639 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4640 _("Are you sure you want to delete the route \"%s\""),
4643 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4647 vik_layer_emit_update ( VIK_LAYER(vtl) );
4651 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4653 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4655 vik_waypoint_set_name ( wp, new_name );
4657 // Now update the treeview as well
4662 // Need key of it for treeview update
4663 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4665 if ( wpf && udataU.uuid ) {
4666 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4669 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4670 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4675 static void trw_layer_properties_item ( gpointer pass_along[7] )
4677 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4678 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4680 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4682 if ( wp && wp->name )
4684 gboolean updated = FALSE;
4685 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4687 trw_layer_waypoint_rename ( vtl, wp, new_name );
4689 if ( updated && pass_along[6] )
4690 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4692 if ( updated && VIK_LAYER(vtl)->visible )
4693 vik_layer_emit_update ( VIK_LAYER(vtl) );
4699 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4700 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4702 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4704 if ( tr && tr->name )
4706 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4709 pass_along[1], /* vlp */
4710 pass_along[5], /* vvp */
4711 pass_along[6]); /* iter */
4717 * Update the treeview of the track id - primarily to update the icon
4719 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4725 gpointer *trkf = NULL;
4726 if ( trk->is_route )
4727 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4729 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4731 if ( trkf && udata.uuid ) {
4733 GtkTreeIter *iter = NULL;
4734 if ( trk->is_route )
4735 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4737 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4740 // TODO: Make this a function
4741 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4742 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4743 ((trk->color.green & 0xff00) << 8) |
4744 (trk->color.blue & 0xff00);
4745 gdk_pixbuf_fill ( pixbuf, pixel );
4746 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4747 g_object_unref (pixbuf);
4754 Parameter 1 -> VikLayersPanel
4755 Parameter 2 -> VikLayer
4756 Parameter 3 -> VikViewport
4758 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4761 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4762 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4765 /* since vlp not set, vl & vvp should be valid instead! */
4767 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4768 vik_layer_emit_update ( VIK_LAYER(vl) );
4773 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4775 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4777 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4778 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4780 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4782 if ( track && track->trackpoints )
4783 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4786 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4788 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4790 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4791 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4793 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4795 if ( track && track->trackpoints )
4797 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4799 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4800 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4801 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4802 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4803 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4807 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4809 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4811 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4812 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4814 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4819 // Converting a track to a route can be a bit more complicated,
4820 // so give a chance to change our minds:
4821 if ( !trk->is_route &&
4822 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4823 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4825 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4826 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4831 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4834 trk_copy->is_route = !trk_copy->is_route;
4836 // ATM can't set name to self - so must create temporary copy
4837 gchar *name = g_strdup ( trk_copy->name );
4839 // Delete old one and then add new one
4840 if ( trk->is_route ) {
4841 vik_trw_layer_delete_route ( vtl, trk );
4842 vik_trw_layer_add_track ( vtl, name, trk_copy );
4845 // Extra route conversion bits...
4846 vik_track_merge_segments ( trk_copy );
4847 vik_track_to_routepoints ( trk_copy );
4849 vik_trw_layer_delete_track ( vtl, trk );
4850 vik_trw_layer_add_route ( vtl, name, trk_copy );
4854 // Update in case color of track / route changes when moving between sublayers
4855 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4859 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4861 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4863 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4864 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4866 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4871 vtl->current_track = track;
4872 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);
4874 if ( track->trackpoints )
4875 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4879 * extend a track using route finder
4881 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4883 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4884 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4887 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4889 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4890 vtl->route_finder_coord = last_coord;
4891 vtl->route_finder_current_track = track;
4892 vtl->route_finder_started = TRUE;
4894 if ( track->trackpoints )
4895 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4899 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4901 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4902 /* Also warn if overwrite old elevation data */
4903 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4905 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4906 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4908 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4911 vik_track_apply_dem_data ( track );
4914 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4916 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4918 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4919 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4921 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4926 GList *trps = track->trackpoints;
4929 trps = g_list_last(trps);
4930 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4933 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4935 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4937 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4938 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4940 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4945 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4948 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4951 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4953 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4955 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4956 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4958 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4963 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4966 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4969 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4971 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4973 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4974 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4976 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4981 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4984 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4988 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4990 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4992 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4994 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4995 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4997 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4999 if ( trk && trk->trackpoints )
5001 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5002 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5003 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5004 if ( pass_along[1] )
5005 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5007 vik_layer_emit_update ( VIK_LAYER(vtl) );
5011 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5013 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5014 trw_layer_tpwin_init ( vtl );
5017 /*************************************
5018 * merge/split by time routines
5019 *************************************/
5021 /* called for each key in track hash table.
5022 * If the current track has the same time stamp type, add it to the result,
5023 * except the one pointed by "exclude".
5024 * set exclude to NULL if there is no exclude to check.
5025 * Note that the result is in reverse (for performance reasons).
5030 gboolean with_timestamps;
5032 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5034 twt_udata *user_data = udata;
5035 VikTrackpoint *p1, *p2;
5037 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5041 if (VIK_TRACK(value)->trackpoints) {
5042 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5043 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5045 if ( user_data->with_timestamps ) {
5046 if (!p1->has_timestamp || !p2->has_timestamp) {
5051 // Don't add tracks with timestamps when getting non timestamp tracks
5052 if (p1->has_timestamp || p2->has_timestamp) {
5058 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5061 /* called for each key in track hash table. if original track user_data[1] is close enough
5062 * to the passed one, add it to list in user_data[0]
5064 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5067 VikTrackpoint *p1, *p2;
5068 VikTrack *trk = VIK_TRACK(value);
5070 GList **nearby_tracks = ((gpointer *)user_data)[0];
5071 GList *tpoints = ((gpointer *)user_data)[1];
5074 * detect reasons for not merging, and return
5075 * if no reason is found not to merge, then do it.
5078 // Exclude the original track from the compiled list
5079 if (trk->trackpoints == tpoints) {
5083 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5084 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5086 if (trk->trackpoints) {
5087 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5088 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5090 if (!p1->has_timestamp || !p2->has_timestamp) {
5091 //g_print("no timestamp\n");
5095 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5096 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5097 if (! (abs(t1 - p2->timestamp) < threshold ||
5099 abs(p1->timestamp - t2) < threshold)
5106 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5109 /* comparison function used to sort tracks; a and b are hash table keys */
5110 /* Not actively used - can be restored if needed
5111 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5113 GHashTable *tracks = user_data;
5116 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5117 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5119 if (t1 < t2) return -1;
5120 if (t1 > t2) return 1;
5125 /* comparison function used to sort trackpoints */
5126 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5128 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5130 if (t1 < t2) return -1;
5131 if (t1 > t2) return 1;
5136 * comparison function which can be used to sort tracks or waypoints by name
5138 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5140 const gchar* namea = (const gchar*) a;
5141 const gchar* nameb = (const gchar*) b;
5142 if ( namea == NULL || nameb == NULL)
5145 // Same sort method as used in the vik_treeview_*_alphabetize functions
5146 return strcmp ( namea, nameb );
5150 * Attempt to merge selected track with other tracks specified by the user
5151 * Tracks to merge with must be of the same 'type' as the selected track -
5152 * either all with timestamps, or all without timestamps
5154 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5156 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5157 GList *other_tracks = NULL;
5158 GHashTable *ght_tracks;
5159 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5160 ght_tracks = vtl->routes;
5162 ght_tracks = vtl->tracks;
5164 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5169 if ( !track->trackpoints )
5173 udata.result = &other_tracks;
5174 udata.exclude = track->trackpoints;
5175 // Allow merging with 'similar' time type time tracks
5176 // i.e. either those times, or those without
5177 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5179 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5180 other_tracks = g_list_reverse(other_tracks);
5182 if ( !other_tracks ) {
5183 if ( udata.with_timestamps )
5184 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5186 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5190 // Sort alphabetically for user presentation
5191 // Convert into list of names for usage with dialog function
5192 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5193 GList *other_tracks_names = NULL;
5194 GList *iter = g_list_first ( other_tracks );
5196 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5197 iter = g_list_next ( iter );
5200 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5202 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5206 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5207 g_list_free(other_tracks);
5208 g_list_free(other_tracks_names);
5213 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5214 VikTrack *merge_track;
5215 if ( track->is_route )
5216 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5218 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5221 vik_track_steal_and_append_trackpoints ( track, merge_track );
5222 if ( track->is_route )
5223 vik_trw_layer_delete_route (vtl, merge_track);
5225 vik_trw_layer_delete_track (vtl, merge_track);
5226 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5229 for (l = merge_list; l != NULL; l = g_list_next(l))
5231 g_list_free(merge_list);
5233 vik_layer_emit_update( VIK_LAYER(vtl) );
5237 // c.f. trw_layer_sorted_track_id_by_name_list
5238 // but don't add the specified track to the list (normally current track)
5239 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5241 twt_udata *user_data = udata;
5244 if (trk->trackpoints == user_data->exclude) {
5248 // Sort named list alphabetically
5249 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5253 * Join - this allows combining 'tracks' and 'track routes'
5254 * i.e. doesn't care about whether tracks have consistent timestamps
5255 * ATM can only append one track at a time to the currently selected track
5257 static void trw_layer_append_track ( gpointer pass_along[6] )
5260 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5262 GHashTable *ght_tracks;
5263 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5264 ght_tracks = vtl->routes;
5266 ght_tracks = vtl->tracks;
5268 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5273 GList *other_tracks_names = NULL;
5275 // Sort alphabetically for user presentation
5276 // Convert into list of names for usage with dialog function
5277 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5279 udata.result = &other_tracks_names;
5280 udata.exclude = trk->trackpoints;
5282 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5284 // Note the limit to selecting one track only
5285 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5286 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5287 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5290 trk->is_route ? _("Append Route"): _("Append Track"),
5291 trk->is_route ? _("Select the route to append after the current route") :
5292 _("Select the track to append after the current track") );
5294 g_list_free(other_tracks_names);
5296 // It's a list, but shouldn't contain more than one other track!
5297 if ( append_list ) {
5299 for (l = append_list; l != NULL; l = g_list_next(l)) {
5300 // TODO: at present this uses the first track found by name,
5301 // which with potential multiple same named tracks may not be the one selected...
5302 VikTrack *append_track;
5303 if ( trk->is_route )
5304 append_track = vik_trw_layer_get_route ( vtl, l->data );
5306 append_track = vik_trw_layer_get_track ( vtl, l->data );
5308 if ( append_track ) {
5309 vik_track_steal_and_append_trackpoints ( trk, append_track );
5310 if ( trk->is_route )
5311 vik_trw_layer_delete_route (vtl, append_track);
5313 vik_trw_layer_delete_track (vtl, append_track);
5316 for (l = append_list; l != NULL; l = g_list_next(l))
5318 g_list_free(append_list);
5320 vik_layer_emit_update( VIK_LAYER(vtl) );
5325 * Very similar to trw_layer_append_track for joining
5326 * but this allows selection from the 'other' list
5327 * If a track is selected, then is shows routes and joins the selected one
5328 * If a route is selected, then is shows tracks and joins the selected one
5330 static void trw_layer_append_other ( gpointer pass_along[6] )
5333 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5335 GHashTable *ght_mykind, *ght_others;
5336 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5337 ght_mykind = vtl->routes;
5338 ght_others = vtl->tracks;
5341 ght_mykind = vtl->tracks;
5342 ght_others = vtl->routes;
5345 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5350 GList *other_tracks_names = NULL;
5352 // Sort alphabetically for user presentation
5353 // Convert into list of names for usage with dialog function
5354 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5356 udata.result = &other_tracks_names;
5357 udata.exclude = trk->trackpoints;
5359 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5361 // Note the limit to selecting one track only
5362 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5363 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5364 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5367 trk->is_route ? _("Append Track"): _("Append Route"),
5368 trk->is_route ? _("Select the track to append after the current route") :
5369 _("Select the route to append after the current track") );
5371 g_list_free(other_tracks_names);
5373 // It's a list, but shouldn't contain more than one other track!
5374 if ( append_list ) {
5376 for (l = append_list; l != NULL; l = g_list_next(l)) {
5377 // TODO: at present this uses the first track found by name,
5378 // which with potential multiple same named tracks may not be the one selected...
5380 // Get FROM THE OTHER TYPE list
5381 VikTrack *append_track;
5382 if ( trk->is_route )
5383 append_track = vik_trw_layer_get_track ( vtl, l->data );
5385 append_track = vik_trw_layer_get_route ( vtl, l->data );
5387 if ( append_track ) {
5389 if ( !append_track->is_route &&
5390 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5391 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5393 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5394 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5395 vik_track_merge_segments ( append_track );
5396 vik_track_to_routepoints ( append_track );
5403 vik_track_steal_and_append_trackpoints ( trk, append_track );
5405 // Delete copied which is FROM THE OTHER TYPE list
5406 if ( trk->is_route )
5407 vik_trw_layer_delete_track (vtl, append_track);
5409 vik_trw_layer_delete_route (vtl, append_track);
5412 for (l = append_list; l != NULL; l = g_list_next(l))
5414 g_list_free(append_list);
5415 vik_layer_emit_update( VIK_LAYER(vtl) );
5419 /* merge by segments */
5420 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5422 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5423 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5424 guint segments = vik_track_merge_segments ( trk );
5425 // NB currently no need to redraw as segments not actually shown on the display
5426 // However inform the user of what happened:
5428 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5429 g_snprintf(str, 64, tmp_str, segments);
5430 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5433 /* merge by time routine */
5434 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5436 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5440 GList *tracks_with_timestamp = NULL;
5441 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5442 if (orig_trk->trackpoints &&
5443 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5444 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5449 udata.result = &tracks_with_timestamp;
5450 udata.exclude = orig_trk->trackpoints;
5451 udata.with_timestamps = TRUE;
5452 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5453 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5455 if (!tracks_with_timestamp) {
5456 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5459 g_list_free(tracks_with_timestamp);
5461 static guint threshold_in_minutes = 1;
5462 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5463 _("Merge Threshold..."),
5464 _("Merge when time between tracks less than:"),
5465 &threshold_in_minutes)) {
5469 // keep attempting to merge all tracks until no merges within the time specified is possible
5470 gboolean attempt_merge = TRUE;
5471 GList *nearby_tracks = NULL;
5473 static gpointer params[3];
5475 while ( attempt_merge ) {
5477 // Don't try again unless tracks have changed
5478 attempt_merge = FALSE;
5480 trps = orig_trk->trackpoints;
5484 if (nearby_tracks) {
5485 g_list_free(nearby_tracks);
5486 nearby_tracks = NULL;
5489 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5490 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5492 /* g_print("Original track times: %d and %d\n", t1, t2); */
5493 params[0] = &nearby_tracks;
5494 params[1] = (gpointer)trps;
5495 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5497 /* get a list of adjacent-in-time tracks */
5498 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5501 GList *l = nearby_tracks;
5504 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5505 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5507 t1 = get_first_trackpoint(l)->timestamp;
5508 t2 = get_last_trackpoint(l)->timestamp;
5509 #undef get_first_trackpoint
5510 #undef get_last_trackpoint
5511 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5514 /* remove trackpoints from merged track, delete track */
5515 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5516 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5518 // Tracks have changed, therefore retry again against all the remaining tracks
5519 attempt_merge = TRUE;
5524 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5527 g_list_free(nearby_tracks);
5529 vik_layer_emit_update( VIK_LAYER(vtl) );
5533 * Split a track at the currently selected trackpoint
5535 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5537 if ( !vtl->current_tpl )
5540 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5541 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5543 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5544 GList *newglist = g_list_alloc ();
5545 newglist->prev = NULL;
5546 newglist->next = vtl->current_tpl->next;
5547 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5548 tr->trackpoints = newglist;
5550 vtl->current_tpl->next->prev = newglist; /* end old track here */
5551 vtl->current_tpl->next = NULL;
5553 // Bounds of the selected track changed due to the split
5554 vik_track_calculate_bounds ( vtl->current_tp_track );
5556 vtl->current_tpl = newglist; /* change tp to first of new track. */
5557 vtl->current_tp_track = tr;
5560 vik_trw_layer_add_route ( vtl, name, tr );
5562 vik_trw_layer_add_track ( vtl, name, tr );
5564 // Bounds of the new track created by the split
5565 vik_track_calculate_bounds ( tr );
5571 // Also need id of newly created track
5574 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5576 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5578 if ( trkf && udata.uuid )
5579 vtl->current_tp_id = udata.uuid;
5581 vtl->current_tp_id = NULL;
5583 vik_layer_emit_update(VIK_LAYER(vtl));
5589 /* split by time routine */
5590 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5592 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5593 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5594 GList *trps = track->trackpoints;
5596 GList *newlists = NULL;
5597 GList *newtps = NULL;
5598 static guint thr = 1;
5605 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5606 _("Split Threshold..."),
5607 _("Split when time between trackpoints exceeds:"),
5612 /* iterate through trackpoints, and copy them into new lists without touching original list */
5613 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5617 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5619 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5622 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5623 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5624 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5626 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5631 if (ts - prev_ts > thr*60) {
5632 /* flush accumulated trackpoints into new list */
5633 newlists = g_list_append(newlists, g_list_reverse(newtps));
5637 /* accumulate trackpoint copies in newtps, in reverse order */
5638 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5640 iter = g_list_next(iter);
5643 newlists = g_list_append(newlists, g_list_reverse(newtps));
5646 /* put lists of trackpoints into tracks */
5648 // Only bother updating if the split results in new tracks
5649 if (g_list_length (newlists) > 1) {
5654 tr = vik_track_copy ( track, FALSE );
5655 tr->trackpoints = (GList *)(iter->data);
5657 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5658 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5659 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5660 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5661 g_free ( new_tr_name );
5662 vik_track_calculate_bounds ( tr );
5663 iter = g_list_next(iter);
5665 // Remove original track and then update the display
5666 vik_trw_layer_delete_track (vtl, track);
5667 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5669 g_list_free(newlists);
5673 * Split a track by the number of points as specified by the user
5675 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5677 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5679 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5680 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5682 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5687 // Check valid track
5688 GList *trps = track->trackpoints;
5692 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5693 _("Split Every Nth Point"),
5694 _("Split on every Nth point:"),
5695 250, // Default value as per typical limited track capacity of various GPS devices
5699 // Was a valid number returned?
5705 GList *newlists = NULL;
5706 GList *newtps = NULL;
5711 /* accumulate trackpoint copies in newtps, in reverse order */
5712 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5714 if (count >= points) {
5715 /* flush accumulated trackpoints into new list */
5716 newlists = g_list_append(newlists, g_list_reverse(newtps));
5720 iter = g_list_next(iter);
5723 // If there is a remaining chunk put that into the new split list
5724 // This may well be the whole track if no split points were encountered
5726 newlists = g_list_append(newlists, g_list_reverse(newtps));
5729 /* put lists of trackpoints into tracks */
5731 // Only bother updating if the split results in new tracks
5732 if (g_list_length (newlists) > 1) {
5737 tr = vik_track_copy ( track, FALSE );
5738 tr->trackpoints = (GList *)(iter->data);
5740 if ( track->is_route ) {
5741 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5742 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5745 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5746 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5748 g_free ( new_tr_name );
5749 vik_track_calculate_bounds ( tr );
5751 iter = g_list_next(iter);
5753 // Remove original track and then update the display
5754 if ( track->is_route )
5755 vik_trw_layer_delete_route (vtl, track);
5757 vik_trw_layer_delete_track (vtl, track);
5758 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5760 g_list_free(newlists);
5764 * Split a track at the currently selected trackpoint
5766 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5768 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5769 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5770 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5774 * Split a track by its segments
5775 * Routes do not have segments so don't call this for routes
5777 static void trw_layer_split_segments ( gpointer pass_along[6] )
5779 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5780 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5787 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5790 for ( i = 0; i < ntracks; i++ ) {
5792 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5793 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5794 g_free ( new_tr_name );
5799 // Remove original track
5800 vik_trw_layer_delete_track ( vtl, trk );
5801 vik_layer_emit_update ( VIK_LAYER(vtl) );
5804 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5807 /* end of split/merge routines */
5810 * Delete adjacent track points at the same position
5811 * AKA Delete Dulplicates on the Properties Window
5813 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5815 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5817 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5818 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5820 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5825 gulong removed = vik_track_remove_dup_points ( trk );
5827 // Track has been updated so update tps:
5828 trw_layer_cancel_tps_of_track ( vtl, trk );
5830 // Inform user how much was deleted as it's not obvious from the normal view
5832 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5833 g_snprintf(str, 64, tmp_str, removed);
5834 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5836 vik_layer_emit_update ( VIK_LAYER(vtl) );
5840 * Delete adjacent track points with the same timestamp
5841 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5843 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5845 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5847 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5848 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5850 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5855 gulong removed = vik_track_remove_same_time_points ( trk );
5857 // Track has been updated so update tps:
5858 trw_layer_cancel_tps_of_track ( vtl, trk );
5860 // Inform user how much was deleted as it's not obvious from the normal view
5862 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5863 g_snprintf(str, 64, tmp_str, removed);
5864 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5866 vik_layer_emit_update ( VIK_LAYER(vtl) );
5872 static void trw_layer_reverse ( gpointer pass_along[6] )
5874 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5876 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5877 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5879 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5884 vik_track_reverse ( track );
5886 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5890 * Similar to trw_layer_enum_item, but this uses a sorted method
5893 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5895 GList **list = (GList**)udata;
5896 // *list = g_list_prepend(*all, key); //unsorted method
5897 // Sort named list alphabetically
5898 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5903 * Now Waypoint specific sort
5905 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5907 GList **list = (GList**)udata;
5908 // Sort named list alphabetically
5909 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5913 * Track specific sort
5915 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5917 GList **list = (GList**)udata;
5918 // Sort named list alphabetically
5919 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5924 gboolean has_same_track_name;
5925 const gchar *same_track_name;
5926 } same_track_name_udata;
5928 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5930 const gchar* namea = (const gchar*) aa;
5931 const gchar* nameb = (const gchar*) bb;
5934 gint result = strcmp ( namea, nameb );
5936 if ( result == 0 ) {
5937 // Found two names the same
5938 same_track_name_udata *user_data = udata;
5939 user_data->has_same_track_name = TRUE;
5940 user_data->same_track_name = namea;
5943 // Leave ordering the same
5948 * Find out if any tracks have the same name in this hash table
5950 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5952 // Sort items by name, then compare if any next to each other are the same
5954 GList *track_names = NULL;
5955 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5958 if ( ! track_names )
5961 same_track_name_udata udata;
5962 udata.has_same_track_name = FALSE;
5964 // Use sort routine to traverse list comparing items
5965 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5966 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5967 // Still no tracks...
5971 return udata.has_same_track_name;
5975 * Force unqiue track names for the track table specified
5976 * Note the panel is a required parameter to enable the update of the names displayed
5977 * Specify if on tracks or else on routes
5979 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5981 // . Search list for an instance of repeated name
5982 // . get track of this name
5983 // . create new name
5984 // . rename track & update equiv. treeview iter
5985 // . repeat until all different
5987 same_track_name_udata udata;
5989 GList *track_names = NULL;
5990 udata.has_same_track_name = FALSE;
5991 udata.same_track_name = NULL;
5993 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5996 if ( ! track_names )
5999 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6001 // Still no tracks...
6002 if ( ! dummy_list1 )
6005 while ( udata.has_same_track_name ) {
6007 // Find a track with the same name
6010 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6012 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6016 g_critical("Houston, we've had a problem.");
6017 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6018 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6023 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6024 vik_track_set_name ( trk, newname );
6030 // Need want key of it for treeview update
6031 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6033 if ( trkf && udataU.uuid ) {
6037 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6039 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6042 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6044 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6046 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6050 // Start trying to find same names again...
6052 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6053 udata.has_same_track_name = FALSE;
6054 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6056 // No tracks any more - give up searching
6057 if ( ! dummy_list2 )
6058 udata.has_same_track_name = FALSE;
6062 vik_layers_panel_emit_update ( vlp );
6065 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6067 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6070 switch (GPOINTER_TO_INT (pass_along[2])) {
6071 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6072 iter = &(vtl->tracks_iter);
6073 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6075 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6076 iter = &(vtl->routes_iter);
6077 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6079 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6080 iter = &(vtl->waypoints_iter);
6081 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6085 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6088 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6090 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6093 switch (GPOINTER_TO_INT (pass_along[2])) {
6094 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6095 iter = &(vtl->tracks_iter);
6096 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6098 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6099 iter = &(vtl->routes_iter);
6100 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6102 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6103 iter = &(vtl->waypoints_iter);
6104 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6108 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6114 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6116 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6119 // Ensure list of track names offered is unique
6120 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6121 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6122 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6123 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6129 // Sort list alphabetically for better presentation
6130 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6133 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6137 // Get list of items to delete from the user
6138 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6141 _("Delete Selection"),
6142 _("Select tracks to delete"));
6145 // Delete requested tracks
6146 // since specificly requested, IMHO no need for extra confirmation
6147 if ( delete_list ) {
6149 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6150 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6151 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6153 g_list_free(delete_list);
6154 vik_layer_emit_update( VIK_LAYER(vtl) );
6161 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6163 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6166 // Ensure list of track names offered is unique
6167 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6168 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6169 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6170 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6176 // Sort list alphabetically for better presentation
6177 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6180 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6184 // Get list of items to delete from the user
6185 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6188 _("Delete Selection"),
6189 _("Select routes to delete") );
6192 // Delete requested routes
6193 // since specificly requested, IMHO no need for extra confirmation
6194 if ( delete_list ) {
6196 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6197 // This deletes first route it finds of that name (but uniqueness is enforced above)
6198 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6200 g_list_free(delete_list);
6201 vik_layer_emit_update( VIK_LAYER(vtl) );
6206 gboolean has_same_waypoint_name;
6207 const gchar *same_waypoint_name;
6208 } same_waypoint_name_udata;
6210 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6212 const gchar* namea = (const gchar*) aa;
6213 const gchar* nameb = (const gchar*) bb;
6216 gint result = strcmp ( namea, nameb );
6218 if ( result == 0 ) {
6219 // Found two names the same
6220 same_waypoint_name_udata *user_data = udata;
6221 user_data->has_same_waypoint_name = TRUE;
6222 user_data->same_waypoint_name = namea;
6225 // Leave ordering the same
6230 * Find out if any waypoints have the same name in this layer
6232 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6234 // Sort items by name, then compare if any next to each other are the same
6236 GList *waypoint_names = NULL;
6237 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6240 if ( ! waypoint_names )
6243 same_waypoint_name_udata udata;
6244 udata.has_same_waypoint_name = FALSE;
6246 // Use sort routine to traverse list comparing items
6247 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6248 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6249 // Still no waypoints...
6253 return udata.has_same_waypoint_name;
6257 * Force unqiue waypoint names for this layer
6258 * Note the panel is a required parameter to enable the update of the names displayed
6260 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6262 // . Search list for an instance of repeated name
6263 // . get waypoint of this name
6264 // . create new name
6265 // . rename waypoint & update equiv. treeview iter
6266 // . repeat until all different
6268 same_waypoint_name_udata udata;
6270 GList *waypoint_names = NULL;
6271 udata.has_same_waypoint_name = FALSE;
6272 udata.same_waypoint_name = NULL;
6274 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6277 if ( ! waypoint_names )
6280 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6282 // Still no waypoints...
6283 if ( ! dummy_list1 )
6286 while ( udata.has_same_waypoint_name ) {
6288 // Find a waypoint with the same name
6289 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6293 g_critical("Houston, we've had a problem.");
6294 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6295 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6300 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6302 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6304 // Start trying to find same names again...
6305 waypoint_names = NULL;
6306 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6307 udata.has_same_waypoint_name = FALSE;
6308 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6310 // No waypoints any more - give up searching
6311 if ( ! dummy_list2 )
6312 udata.has_same_waypoint_name = FALSE;
6316 vik_layers_panel_emit_update ( vlp );
6322 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6324 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6327 // Ensure list of waypoint names offered is unique
6328 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6329 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6330 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6331 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6337 // Sort list alphabetically for better presentation
6338 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6340 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6344 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6346 // Get list of items to delete from the user
6347 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6350 _("Delete Selection"),
6351 _("Select waypoints to delete"));
6354 // Delete requested waypoints
6355 // since specificly requested, IMHO no need for extra confirmation
6356 if ( delete_list ) {
6358 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6359 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6360 trw_layer_delete_waypoint_by_name (vtl, l->data);
6362 g_list_free(delete_list);
6364 trw_layer_calculate_bounds_waypoints ( vtl );
6365 vik_layer_emit_update( VIK_LAYER(vtl) );
6373 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6375 vik_treeview_item_toggle_visible ( vt, it );
6381 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6383 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6389 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6391 wp->visible = GPOINTER_TO_INT (on_off);
6397 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6399 wp->visible = !wp->visible;
6405 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6407 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6408 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6409 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6410 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6412 vik_layer_emit_update ( VIK_LAYER(vtl) );
6418 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6420 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6421 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6422 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6423 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6425 vik_layer_emit_update ( VIK_LAYER(vtl) );
6431 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6433 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6434 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6435 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6437 vik_layer_emit_update ( VIK_LAYER(vtl) );
6443 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6445 trk->visible = GPOINTER_TO_INT (on_off);
6451 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6453 trk->visible = !trk->visible;
6459 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6461 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6462 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6463 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6464 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6466 vik_layer_emit_update ( VIK_LAYER(vtl) );
6472 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6474 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6475 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6476 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6477 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6479 vik_layer_emit_update ( VIK_LAYER(vtl) );
6485 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6487 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6488 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6489 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6491 vik_layer_emit_update ( VIK_LAYER(vtl) );
6497 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6499 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6500 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6501 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6502 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6504 vik_layer_emit_update ( VIK_LAYER(vtl) );
6510 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6512 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6513 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6514 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6515 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6517 vik_layer_emit_update ( VIK_LAYER(vtl) );
6523 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6525 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6526 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6527 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6529 vik_layer_emit_update ( VIK_LAYER(vtl) );
6533 * trw_layer_analyse_close:
6535 * Stuff to do on dialog closure
6537 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6539 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6540 gtk_widget_destroy ( dialog );
6541 vtl->tracks_analysis_dialog = NULL;
6545 * trw_layer_analyse_create_list:
6547 * Create the latest list of tracks with the associated layer(s)
6548 * Although this will always be from a single layer here
6550 static GList* trw_layer_analyse_create_list ( VikLayer *vl, gpointer user_data )
6552 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6553 GList *tracks = NULL;
6554 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6555 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6557 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6559 GList *tracks_and_layers = NULL;
6560 // build tracks_and_layers list
6561 tracks = g_list_first ( tracks );
6563 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6564 vtdl->trk = VIK_TRACK(tracks->data);
6566 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6567 tracks = g_list_next ( tracks );
6570 return tracks_and_layers;
6573 static void trw_layer_tracks_stats ( gpointer lav[2] )
6575 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6576 // There can only be one!
6577 if ( vtl->tracks_analysis_dialog )
6580 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6581 VIK_LAYER(vtl)->name,
6583 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6584 trw_layer_analyse_create_list,
6585 trw_layer_analyse_close );
6591 static void trw_layer_routes_stats ( gpointer lav[2] )
6593 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6594 // There can only be one!
6595 if ( vtl->tracks_analysis_dialog )
6598 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6599 VIK_LAYER(vtl)->name,
6601 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6602 trw_layer_analyse_create_list,
6603 trw_layer_analyse_close );
6606 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6608 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6610 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6613 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6615 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6618 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6619 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6623 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6625 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6628 if ( !strncmp(wp->comment, "http", 4) ) {
6629 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6630 } else if ( !strncmp(wp->description, "http", 4) ) {
6631 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6635 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6637 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6639 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6641 // No actual change to the name supplied
6643 if (strcmp(newname, wp->name) == 0 )
6646 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6649 // An existing waypoint has been found with the requested name
6650 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6651 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6656 // Update WP name and refresh the treeview
6657 vik_waypoint_set_name (wp, newname);
6659 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6660 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
6662 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6667 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6669 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6671 // No actual change to the name supplied
6673 if (strcmp(newname, trk->name) == 0)
6676 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6679 // An existing track has been found with the requested name
6680 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6681 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6685 // Update track name and refresh GUI parts
6686 vik_track_set_name (trk, newname);
6688 // Update any subwindows that could be displaying this track which has changed name
6689 // Only one Track Edit Window
6690 if ( l->current_tp_track == trk && l->tpwin ) {
6691 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6693 // Property Dialog of the track
6694 vik_trw_layer_propwin_update ( trk );
6696 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6697 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6699 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6704 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6706 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6708 // No actual change to the name supplied
6710 if (strcmp(newname, trk->name) == 0)
6713 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6716 // An existing track has been found with the requested name
6717 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6718 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6722 // Update track name and refresh GUI parts
6723 vik_track_set_name (trk, newname);
6725 // Update any subwindows that could be displaying this track which has changed name
6726 // Only one Track Edit Window
6727 if ( l->current_tp_track == trk && l->tpwin ) {
6728 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6730 // Property Dialog of the track
6731 vik_trw_layer_propwin_update ( trk );
6733 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6734 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6736 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6743 static gboolean is_valid_geocache_name ( gchar *str )
6745 gint len = strlen ( str );
6746 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]));
6749 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6751 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6752 a_acquire_set_filter_track ( trk );
6755 #ifdef VIK_CONFIG_GOOGLE
6756 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6758 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6759 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6762 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6764 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6766 gchar *escaped = uri_escape ( tr->comment );
6767 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6768 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6775 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6776 /* viewpoint is now available instead */
6777 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6779 static gpointer pass_along[8];
6781 gboolean rv = FALSE;
6784 pass_along[1] = vlp;
6785 pass_along[2] = GINT_TO_POINTER (subtype);
6786 pass_along[3] = sublayer;
6787 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6788 pass_along[5] = vvp;
6789 pass_along[6] = iter;
6790 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6792 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6796 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6797 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6798 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6799 gtk_widget_show ( item );
6801 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6802 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6803 if (tr && tr->property_dialog)
6804 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6806 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6807 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6808 if (tr && tr->property_dialog)
6809 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6812 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6814 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6815 gtk_widget_show ( item );
6817 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6819 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6820 gtk_widget_show ( item );
6822 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6823 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6824 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6825 gtk_widget_show ( item );
6827 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6829 gboolean separator_created = FALSE;
6831 /* could be a right-click using the tool */
6832 if ( vlp != NULL ) {
6833 item = gtk_menu_item_new ();
6834 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6835 gtk_widget_show ( item );
6837 separator_created = TRUE;
6839 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6840 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6842 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6843 gtk_widget_show ( item );
6846 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6848 if ( wp && wp->name ) {
6849 if ( is_valid_geocache_name ( wp->name ) ) {
6851 if ( !separator_created ) {
6852 item = gtk_menu_item_new ();
6853 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6854 gtk_widget_show ( item );
6855 separator_created = TRUE;
6858 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6859 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6860 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6861 gtk_widget_show ( item );
6865 if ( wp && wp->image )
6867 if ( !separator_created ) {
6868 item = gtk_menu_item_new ();
6869 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6870 gtk_widget_show ( item );
6871 separator_created = TRUE;
6874 // Set up image paramater
6875 pass_along[5] = wp->image;
6877 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6878 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
6879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6880 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6881 gtk_widget_show ( item );
6883 #ifdef VIK_CONFIG_GEOTAG
6884 GtkWidget *geotag_submenu = gtk_menu_new ();
6885 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6887 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6888 gtk_widget_show ( item );
6889 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6891 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6892 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6893 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6894 gtk_widget_show ( item );
6896 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6898 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6899 gtk_widget_show ( item );
6905 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6906 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6907 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6908 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6909 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6910 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6911 gtk_widget_show ( item );
6918 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6919 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6920 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6921 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6922 gtk_widget_show ( item );
6923 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6924 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6925 gtk_widget_set_sensitive ( item, TRUE );
6927 gtk_widget_set_sensitive ( item, FALSE );
6930 item = gtk_menu_item_new ();
6931 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6932 gtk_widget_show ( item );
6935 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6938 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6939 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6941 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6942 gtk_widget_show ( item );
6945 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6947 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6948 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6949 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6950 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6951 gtk_widget_show ( item );
6953 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6954 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6955 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6956 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6957 gtk_widget_show ( item );
6959 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6960 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6961 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6962 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6963 gtk_widget_show ( item );
6965 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6966 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6968 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6969 gtk_widget_show ( item );
6971 GtkWidget *vis_submenu = gtk_menu_new ();
6972 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6973 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6974 gtk_widget_show ( item );
6975 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6977 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
6978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
6980 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6981 gtk_widget_show ( item );
6983 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
6984 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
6986 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6987 gtk_widget_show ( item );
6989 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6990 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6991 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
6992 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6993 gtk_widget_show ( item );
6996 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7000 if ( l->current_track && !l->current_track->is_route ) {
7001 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7003 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7004 gtk_widget_show ( item );
7006 item = gtk_menu_item_new ();
7007 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7008 gtk_widget_show ( item );
7011 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7012 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7013 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7014 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7015 gtk_widget_show ( item );
7017 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7018 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7020 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7021 gtk_widget_show ( item );
7022 // Make it available only when a new track *not* already in progress
7023 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7025 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7026 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7028 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7029 gtk_widget_show ( item );
7031 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7032 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7034 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7035 gtk_widget_show ( item );
7037 GtkWidget *vis_submenu = gtk_menu_new ();
7038 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7039 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7040 gtk_widget_show ( item );
7041 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7043 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7044 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7045 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7046 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7047 gtk_widget_show ( item );
7049 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7052 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7053 gtk_widget_show ( item );
7055 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7056 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7058 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7059 gtk_widget_show ( item );
7061 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7063 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7064 gtk_widget_show ( item );
7067 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7071 if ( l->current_track && l->current_track->is_route ) {
7072 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7073 // Reuse finish track method
7074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7075 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7076 gtk_widget_show ( item );
7078 item = gtk_menu_item_new ();
7079 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7080 gtk_widget_show ( item );
7083 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7084 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7085 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7086 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7087 gtk_widget_show ( item );
7089 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7090 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7091 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7092 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7093 gtk_widget_show ( item );
7094 // Make it available only when a new track *not* already in progress
7095 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7097 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7098 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7100 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7101 gtk_widget_show ( item );
7103 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7104 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7106 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7107 gtk_widget_show ( item );
7109 GtkWidget *vis_submenu = gtk_menu_new ();
7110 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7111 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7112 gtk_widget_show ( item );
7113 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7115 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7116 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7117 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7118 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7119 gtk_widget_show ( item );
7121 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7124 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7125 gtk_widget_show ( item );
7127 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7128 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7129 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7130 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7131 gtk_widget_show ( item );
7133 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7134 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7135 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7136 gtk_widget_show ( item );
7140 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7141 GtkWidget *submenu_sort = gtk_menu_new ();
7142 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7144 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7145 gtk_widget_show ( item );
7146 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7148 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7151 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7152 gtk_widget_show ( item );
7154 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7155 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7156 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7157 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7158 gtk_widget_show ( item );
7161 GtkWidget *upload_submenu = gtk_menu_new ();
7163 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7165 item = gtk_menu_item_new ();
7166 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7167 gtk_widget_show ( item );
7169 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7170 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7171 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7172 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7173 if ( l->current_track ) {
7174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7175 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7176 gtk_widget_show ( item );
7179 item = gtk_menu_item_new ();
7180 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7181 gtk_widget_show ( item );
7184 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7185 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7187 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7188 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7189 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7190 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7191 gtk_widget_show ( item );
7193 GtkWidget *goto_submenu;
7194 goto_submenu = gtk_menu_new ();
7195 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7196 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7197 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7198 gtk_widget_show ( item );
7199 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7201 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7202 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7204 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7205 gtk_widget_show ( item );
7207 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7208 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7209 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7210 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7211 gtk_widget_show ( item );
7213 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7216 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7217 gtk_widget_show ( item );
7219 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7220 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7222 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7223 gtk_widget_show ( item );
7225 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7226 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7228 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7229 gtk_widget_show ( item );
7231 // Routes don't have speeds
7232 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7233 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7234 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7235 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7236 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7237 gtk_widget_show ( item );
7240 GtkWidget *combine_submenu;
7241 combine_submenu = gtk_menu_new ();
7242 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7243 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7244 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7245 gtk_widget_show ( item );
7246 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7248 // Routes don't have times or segments...
7249 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7250 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7251 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7252 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7253 gtk_widget_show ( item );
7255 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7256 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7257 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7258 gtk_widget_show ( item );
7261 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7262 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7263 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7264 gtk_widget_show ( item );
7266 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7267 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7269 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7271 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7272 gtk_widget_show ( item );
7274 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7275 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7277 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7279 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7280 gtk_widget_show ( item );
7282 GtkWidget *split_submenu;
7283 split_submenu = gtk_menu_new ();
7284 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7286 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7287 gtk_widget_show ( item );
7288 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7290 // Routes don't have times or segments...
7291 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7292 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7294 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7295 gtk_widget_show ( item );
7297 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7298 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7299 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7300 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7301 gtk_widget_show ( item );
7304 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7305 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7306 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7307 gtk_widget_show ( item );
7309 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7311 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7312 gtk_widget_show ( item );
7313 // Make it available only when a trackpoint is selected.
7314 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7316 GtkWidget *delete_submenu;
7317 delete_submenu = gtk_menu_new ();
7318 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7319 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7320 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7321 gtk_widget_show ( item );
7322 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7324 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7325 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7326 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7327 gtk_widget_show ( item );
7329 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7330 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7331 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7332 gtk_widget_show ( item );
7334 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7335 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7337 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7338 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7340 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7341 gtk_widget_show ( item );
7343 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7345 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7346 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7348 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7349 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
7350 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7351 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7352 gtk_widget_show ( item );
7355 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7356 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
7357 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
7358 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7359 gtk_widget_show ( item );
7361 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7362 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7364 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7365 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7366 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7367 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7368 gtk_widget_show ( item );
7370 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7371 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7373 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7374 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7376 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7377 gtk_widget_show ( item );
7379 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7380 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7382 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7383 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7384 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7385 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7386 gtk_widget_show ( item );
7388 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7389 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7390 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
7391 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7392 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7393 gtk_widget_show ( item );
7396 // ATM can't upload a single waypoint but can do waypoints to a GPS
7397 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7398 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7399 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7400 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7401 gtk_widget_show ( item );
7402 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7404 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7405 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7406 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7407 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7408 gtk_widget_show ( item );
7412 #ifdef VIK_CONFIG_GOOGLE
7413 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7415 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7416 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7417 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7418 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7419 gtk_widget_show ( item );
7423 // Some things aren't usable with routes
7424 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7425 #ifdef VIK_CONFIG_OPENSTREETMAP
7426 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7427 // Convert internal pointer into actual track for usage outside this file
7428 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7429 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7430 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7431 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7432 gtk_widget_show ( item );
7435 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7436 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7438 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7439 gtk_widget_show ( item );
7441 /* ATM This function is only available via the layers panel, due to needing a vlp */
7443 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7444 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7445 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7447 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7448 gtk_widget_show ( item );
7452 #ifdef VIK_CONFIG_GEOTAG
7453 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7455 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7456 gtk_widget_show ( item );
7460 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7461 // Only show on viewport popmenu when a trackpoint is selected
7462 if ( ! vlp && l->current_tpl ) {
7464 item = gtk_menu_item_new ();
7465 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7466 gtk_widget_show ( item );
7468 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7469 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7470 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7471 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7472 gtk_widget_show ( item );
7479 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
7482 if (!vtl->current_tpl)
7484 if (!vtl->current_tpl->next)
7487 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7488 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
7490 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
7493 VikTrackpoint *tp_new = vik_trackpoint_new();
7494 struct LatLon ll_current, ll_next;
7495 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
7496 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
7498 /* main positional interpolation */
7499 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
7500 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
7502 /* Now other properties that can be interpolated */
7503 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
7505 if (tp_current->has_timestamp && tp_next->has_timestamp) {
7506 /* Note here the division is applied to each part, then added
7507 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
7508 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
7509 tp_new->has_timestamp = TRUE;
7512 if (tp_current->speed != NAN && tp_next->speed != NAN)
7513 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
7515 /* TODO - improve interpolation of course, as it may not be correct.
7516 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
7517 [similar applies if value is in radians] */
7518 if (tp_current->course != NAN && tp_next->course != NAN)
7519 tp_new->course = (tp_current->course + tp_next->course)/2;
7521 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
7523 /* Insert new point into the trackpoints list after the current TP */
7524 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7526 // Otherwise try routes
7527 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7531 gint index = g_list_index ( trk->trackpoints, tp_current );
7533 // NB no recalculation of bounds since it is inserted between points
7534 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
7539 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
7545 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
7549 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
7551 if ( vtl->current_tpl )
7553 vtl->current_tpl = NULL;
7554 vtl->current_tp_track = NULL;
7555 vtl->current_tp_id = NULL;
7556 vik_layer_emit_update(VIK_LAYER(vtl));
7560 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
7562 g_assert ( vtl->tpwin != NULL );
7563 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
7564 trw_layer_cancel_current_tp ( vtl, TRUE );
7566 if ( vtl->current_tpl == NULL )
7569 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
7571 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
7572 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7574 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
7576 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7578 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7584 // Find available adjacent trackpoint
7585 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
7587 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
7588 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
7590 // Delete current trackpoint
7591 vik_trackpoint_free ( vtl->current_tpl->data );
7592 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7594 // Set to current to the available adjacent trackpoint
7595 vtl->current_tpl = new_tpl;
7597 // Reset dialog with the available adjacent trackpoint
7598 if ( vtl->current_tp_track ) {
7599 vik_track_calculate_bounds ( vtl->current_tp_track );
7600 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
7603 vik_layer_emit_update(VIK_LAYER(vtl));
7607 // Delete current trackpoint
7608 vik_trackpoint_free ( vtl->current_tpl->data );
7609 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7610 trw_layer_cancel_current_tp ( vtl, FALSE );
7613 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
7615 if ( vtl->current_tp_track )
7616 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
7617 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
7619 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
7621 if ( vtl->current_tp_track )
7622 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
7623 vik_layer_emit_update(VIK_LAYER(vtl));
7625 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
7627 trw_layer_insert_tp_after_current_tp ( vtl );
7628 vik_layer_emit_update(VIK_LAYER(vtl));
7630 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
7631 vik_layer_emit_update(VIK_LAYER(vtl));
7635 * trw_layer_dialog_shift:
7636 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
7638 * Try to reposition a dialog if it's over the specified coord
7639 * so to not obscure the item of interest
7641 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
7643 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
7645 // Attempt force dialog to be shown so we can find out where it is more reliably...
7646 while ( gtk_events_pending() )
7647 gtk_main_iteration ();
7649 // get parent window position & size
7650 gint win_pos_x, win_pos_y;
7651 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
7653 gint win_size_x, win_size_y;
7654 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
7656 // get own dialog size
7657 gint dia_size_x, dia_size_y;
7658 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
7660 // get own dialog position
7661 gint dia_pos_x, dia_pos_y;
7662 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
7664 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
7665 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
7667 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
7669 gint vp_xx, vp_yy; // In viewport pixels
7670 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
7672 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
7676 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
7678 // Transform Viewport pixels into absolute pixels
7679 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
7680 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
7682 // Is dialog over the point (to within an ^^ edge value)
7683 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
7684 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
7688 gint hh = vik_viewport_get_height ( vvp );
7690 // Consider the difference in viewport to the full window
7691 gint offset_y = dest_y;
7692 // Add difference between dialog and window sizes
7693 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
7695 if ( vp_yy > hh/2 ) {
7696 // Point in bottom half, move window to top half
7697 gtk_window_move ( dialog, dia_pos_x, offset_y );
7700 // Point in top half, move dialog down
7701 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
7705 // Shift left<->right
7706 gint ww = vik_viewport_get_width ( vvp );
7708 // Consider the difference in viewport to the full window
7709 gint offset_x = dest_x;
7710 // Add difference between dialog and window sizes
7711 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
7713 if ( vp_xx > ww/2 ) {
7714 // Point on right, move window to left
7715 gtk_window_move ( dialog, offset_x, dia_pos_y );
7718 // Point on left, move right
7719 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7727 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7731 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7732 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7733 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7734 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7736 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7738 if ( vtl->current_tpl ) {
7739 // get tp pixel position
7740 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7742 // Shift up<->down to try not to obscure the trackpoint.
7743 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7747 if ( vtl->current_tpl )
7748 if ( vtl->current_tp_track )
7749 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7750 /* set layer name and TP data */
7753 /***************************************************************************
7755 ***************************************************************************/
7757 /*** Utility data structures and functions ****/
7761 gint closest_x, closest_y;
7762 gboolean draw_images;
7763 gpointer *closest_wp_id;
7764 VikWaypoint *closest_wp;
7770 gint closest_x, closest_y;
7771 gpointer closest_track_id;
7772 VikTrackpoint *closest_tp;
7778 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7784 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7786 // If waypoint has an image then use the image size to select
7787 if ( params->draw_images && wp->image ) {
7788 gint slackx, slacky;
7789 slackx = wp->image_width / 2;
7790 slacky = wp->image_height / 2;
7792 if ( x <= params->x + slackx && x >= params->x - slackx
7793 && y <= params->y + slacky && y >= params->y - slacky ) {
7794 params->closest_wp_id = id;
7795 params->closest_wp = wp;
7796 params->closest_x = x;
7797 params->closest_y = y;
7800 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7801 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7802 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7804 params->closest_wp_id = id;
7805 params->closest_wp = wp;
7806 params->closest_x = x;
7807 params->closest_y = y;
7811 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7813 GList *tpl = t->trackpoints;
7819 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7825 tp = VIK_TRACKPOINT(tpl->data);
7827 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7829 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7830 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7831 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7833 params->closest_track_id = id;
7834 params->closest_tp = tp;
7835 params->closest_tpl = tpl;
7836 params->closest_x = x;
7837 params->closest_y = y;
7843 // ATM: Leave this as 'Track' only.
7844 // Not overly bothered about having a snap to route trackpoint capability
7845 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7847 TPSearchParams params;
7851 params.closest_track_id = NULL;
7852 params.closest_tp = NULL;
7853 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7854 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7855 return params.closest_tp;
7858 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7860 WPSearchParams params;
7864 params.draw_images = vtl->drawimages;
7865 params.closest_wp = NULL;
7866 params.closest_wp_id = NULL;
7867 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7868 return params.closest_wp;
7872 // Some forward declarations
7873 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7874 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7875 static void marker_end_move ( tool_ed_t *t );
7878 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7882 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7884 // Here always allow snapping back to the original location
7885 // this is useful when one decides not to move the thing afterall
7886 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7889 if ( event->state & GDK_CONTROL_MASK )
7891 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7893 new_coord = tp->coord;
7897 if ( event->state & GDK_SHIFT_MASK )
7899 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7901 new_coord = wp->coord;
7905 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7907 marker_moveto ( t, x, y );
7914 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7916 if ( t->holding && event->button == 1 )
7919 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7922 if ( event->state & GDK_CONTROL_MASK )
7924 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7926 new_coord = tp->coord;
7930 if ( event->state & GDK_SHIFT_MASK )
7932 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7934 new_coord = wp->coord;
7937 marker_end_move ( t );
7939 // Determine if working on a waypoint or a trackpoint
7940 if ( t->is_waypoint ) {
7941 vtl->current_wp->coord = new_coord;
7942 trw_layer_calculate_bounds_waypoints ( vtl );
7945 if ( vtl->current_tpl ) {
7946 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7948 if ( vtl->current_tp_track )
7949 vik_track_calculate_bounds ( vtl->current_tp_track );
7952 if ( vtl->current_tp_track )
7953 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7958 vtl->current_wp = NULL;
7959 vtl->current_wp_id = NULL;
7960 trw_layer_cancel_current_tp ( vtl, FALSE );
7962 vik_layer_emit_update ( VIK_LAYER(vtl) );
7969 Returns true if a waypoint or track is found near the requested event position for this particular layer
7970 The item found is automatically selected
7971 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7973 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7975 if ( event->button != 1 )
7978 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7981 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7985 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7987 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7989 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7990 WPSearchParams wp_params;
7991 wp_params.vvp = vvp;
7992 wp_params.x = event->x;
7993 wp_params.y = event->y;
7994 wp_params.draw_images = vtl->drawimages;
7995 wp_params.closest_wp_id = NULL;
7996 wp_params.closest_wp = NULL;
7998 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8000 if ( wp_params.closest_wp ) {
8003 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8005 // Too easy to move it so must be holding shift to start immediately moving it
8006 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8007 if ( event->state & GDK_SHIFT_MASK ||
8008 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8009 // Put into 'move buffer'
8010 // NB vvp & vw already set in tet
8011 tet->vtl = (gpointer)vtl;
8012 tet->is_waypoint = TRUE;
8014 marker_begin_move (tet, event->x, event->y);
8017 vtl->current_wp = wp_params.closest_wp;
8018 vtl->current_wp_id = wp_params.closest_wp_id;
8020 vik_layer_emit_update ( VIK_LAYER(vtl) );
8026 // Used for both track and route lists
8027 TPSearchParams tp_params;
8028 tp_params.vvp = vvp;
8029 tp_params.x = event->x;
8030 tp_params.y = event->y;
8031 tp_params.closest_track_id = NULL;
8032 tp_params.closest_tp = NULL;
8033 tp_params.closest_tpl = NULL;
8034 tp_params.bbox = bbox;
8036 if (vtl->tracks_visible) {
8037 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8039 if ( tp_params.closest_tp ) {
8041 // Always select + highlight the track
8042 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8044 tet->is_waypoint = FALSE;
8046 // Select the Trackpoint
8047 // Can move it immediately when control held or it's the previously selected tp
8048 if ( event->state & GDK_CONTROL_MASK ||
8049 vtl->current_tpl == tp_params.closest_tpl ) {
8050 // Put into 'move buffer'
8051 // NB vvp & vw already set in tet
8052 tet->vtl = (gpointer)vtl;
8053 marker_begin_move (tet, event->x, event->y);
8056 vtl->current_tpl = tp_params.closest_tpl;
8057 vtl->current_tp_id = tp_params.closest_track_id;
8058 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8060 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8063 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8065 vik_layer_emit_update ( VIK_LAYER(vtl) );
8070 // Try again for routes
8071 if (vtl->routes_visible) {
8072 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8074 if ( tp_params.closest_tp ) {
8076 // Always select + highlight the track
8077 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8079 tet->is_waypoint = FALSE;
8081 // Select the Trackpoint
8082 // Can move it immediately when control held or it's the previously selected tp
8083 if ( event->state & GDK_CONTROL_MASK ||
8084 vtl->current_tpl == tp_params.closest_tpl ) {
8085 // Put into 'move buffer'
8086 // NB vvp & vw already set in tet
8087 tet->vtl = (gpointer)vtl;
8088 marker_begin_move (tet, event->x, event->y);
8091 vtl->current_tpl = tp_params.closest_tpl;
8092 vtl->current_tp_id = tp_params.closest_track_id;
8093 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8095 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8098 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8100 vik_layer_emit_update ( VIK_LAYER(vtl) );
8105 /* these aren't the droids you're looking for */
8106 vtl->current_wp = NULL;
8107 vtl->current_wp_id = NULL;
8108 trw_layer_cancel_current_tp ( vtl, FALSE );
8111 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8116 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8118 if ( event->button != 3 )
8121 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8124 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8127 /* Post menu for the currently selected item */
8129 /* See if a track is selected */
8130 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8131 if ( track && track->visible ) {
8133 if ( track->name ) {
8135 if ( vtl->track_right_click_menu )
8136 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8138 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8145 if ( track->is_route )
8146 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8148 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8150 if ( trkf && udataU.uuid ) {
8153 if ( track->is_route )
8154 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8156 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8158 trw_layer_sublayer_add_menu_items ( vtl,
8159 vtl->track_right_click_menu,
8161 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8167 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8173 /* See if a waypoint is selected */
8174 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8175 if ( waypoint && waypoint->visible ) {
8176 if ( waypoint->name ) {
8178 if ( vtl->wp_right_click_menu )
8179 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8181 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8184 udata.wp = waypoint;
8187 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8189 if ( wpf && udata.uuid ) {
8190 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8192 trw_layer_sublayer_add_menu_items ( vtl,
8193 vtl->wp_right_click_menu,
8195 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8200 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8209 /* background drawing hook, to be passed the viewport */
8210 static gboolean tool_sync_done = TRUE;
8212 static gboolean tool_sync(gpointer data)
8214 VikViewport *vvp = data;
8215 gdk_threads_enter();
8216 vik_viewport_sync(vvp);
8217 tool_sync_done = TRUE;
8218 gdk_threads_leave();
8222 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8225 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8226 gdk_gc_set_function ( t->gc, GDK_INVERT );
8227 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8228 vik_viewport_sync(t->vvp);
8233 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8235 VikViewport *vvp = t->vvp;
8236 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8237 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8241 if (tool_sync_done) {
8242 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8243 tool_sync_done = FALSE;
8247 static void marker_end_move ( tool_ed_t *t )
8249 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8250 g_object_unref ( t->gc );
8254 /*** Edit waypoint ****/
8256 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8258 tool_ed_t *t = g_new(tool_ed_t, 1);
8264 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8269 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8271 WPSearchParams params;
8272 tool_ed_t *t = data;
8273 VikViewport *vvp = t->vvp;
8275 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8282 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8285 if ( vtl->current_wp && vtl->current_wp->visible )
8287 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8289 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8291 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8292 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8294 if ( event->button == 3 )
8295 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8297 marker_begin_move(t, event->x, event->y);
8304 params.x = event->x;
8305 params.y = event->y;
8306 params.draw_images = vtl->drawimages;
8307 params.closest_wp_id = NULL;
8308 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8309 params.closest_wp = NULL;
8310 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8311 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8313 // how do we get here?
8314 marker_begin_move(t, event->x, event->y);
8315 g_critical("shouldn't be here");
8318 else if ( params.closest_wp )
8320 if ( event->button == 3 )
8321 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8323 vtl->waypoint_rightclick = FALSE;
8325 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8327 vtl->current_wp = params.closest_wp;
8328 vtl->current_wp_id = params.closest_wp_id;
8330 /* could make it so don't update if old WP is off screen and new is null but oh well */
8331 vik_layer_emit_update ( VIK_LAYER(vtl) );
8335 vtl->current_wp = NULL;
8336 vtl->current_wp_id = NULL;
8337 vtl->waypoint_rightclick = FALSE;
8338 vik_layer_emit_update ( VIK_LAYER(vtl) );
8342 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8344 tool_ed_t *t = data;
8345 VikViewport *vvp = t->vvp;
8347 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8352 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8355 if ( event->state & GDK_CONTROL_MASK )
8357 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8359 new_coord = tp->coord;
8363 if ( event->state & GDK_SHIFT_MASK )
8365 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8366 if ( wp && wp != vtl->current_wp )
8367 new_coord = wp->coord;
8372 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8374 marker_moveto ( t, x, y );
8381 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8383 tool_ed_t *t = data;
8384 VikViewport *vvp = t->vvp;
8386 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8389 if ( t->holding && event->button == 1 )
8392 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8395 if ( event->state & GDK_CONTROL_MASK )
8397 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8399 new_coord = tp->coord;
8403 if ( event->state & GDK_SHIFT_MASK )
8405 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8406 if ( wp && wp != vtl->current_wp )
8407 new_coord = wp->coord;
8410 marker_end_move ( t );
8412 vtl->current_wp->coord = new_coord;
8414 trw_layer_calculate_bounds_waypoints ( vtl );
8415 vik_layer_emit_update ( VIK_LAYER(vtl) );
8418 /* PUT IN RIGHT PLACE!!! */
8419 if ( event->button == 3 && vtl->waypoint_rightclick )
8421 if ( vtl->wp_right_click_menu )
8422 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8423 if ( vtl->current_wp ) {
8424 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8425 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 );
8426 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8428 vtl->waypoint_rightclick = FALSE;
8433 /*** New track ****/
8435 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8442 GdkDrawable *drawable;
8448 * Draw specified pixmap
8450 static gboolean draw_sync ( gpointer data )
8452 draw_sync_t *ds = (draw_sync_t*) data;
8453 // Sometimes don't want to draw
8454 // normally because another update has taken precedent such as panning the display
8455 // which means this pixmap is no longer valid
8456 if ( ds->vtl->draw_sync_do ) {
8457 gdk_threads_enter();
8458 gdk_draw_drawable (ds->drawable,
8461 0, 0, 0, 0, -1, -1);
8462 ds->vtl->draw_sync_done = TRUE;
8463 gdk_threads_leave();
8469 static gchar* distance_string (gdouble distance)
8473 /* draw label with distance */
8474 vik_units_distance_t dist_units = a_vik_get_units_distance ();
8475 switch (dist_units) {
8476 case VIK_UNITS_DISTANCE_MILES:
8477 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8478 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8479 } else if (distance < 1609.4) {
8480 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8482 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8486 // VIK_UNITS_DISTANCE_KILOMETRES
8487 if (distance >= 1000 && distance < 100000) {
8488 g_sprintf(str, "%3.2f km", distance/1000.0);
8489 } else if (distance < 1000) {
8490 g_sprintf(str, "%d m", (int)distance);
8492 g_sprintf(str, "%d km", (int)distance/1000);
8496 return g_strdup (str);
8500 * Actually set the message in statusbar
8502 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8504 // Only show elevation data when track has some elevation properties
8505 gchar str_gain_loss[64];
8506 str_gain_loss[0] = '\0';
8507 gchar str_last_step[64];
8508 str_last_step[0] = '\0';
8509 gchar *str_total = distance_string (distance);
8511 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
8512 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
8513 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
8515 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
8518 if ( last_step > 0 ) {
8519 gchar *tmp = distance_string (last_step);
8520 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
8524 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8526 // Write with full gain/loss information
8527 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
8528 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8530 g_free ( str_total );
8534 * Figure out what information should be set in the statusbar and then write it
8536 static void update_statusbar ( VikTrwLayer *vtl )
8538 // Get elevation data
8539 gdouble elev_gain, elev_loss;
8540 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8542 /* Find out actual distance of current track */
8543 gdouble distance = vik_track_get_length (vtl->current_track);
8545 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8549 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
8551 /* if we haven't sync'ed yet, we don't have time to do more. */
8552 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
8553 GList *iter = g_list_last ( vtl->current_track->trackpoints );
8554 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
8556 static GdkPixmap *pixmap = NULL;
8558 // Need to check in case window has been resized
8559 w1 = vik_viewport_get_width(vvp);
8560 h1 = vik_viewport_get_height(vvp);
8562 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8564 gdk_drawable_get_size (pixmap, &w2, &h2);
8565 if (w1 != w2 || h1 != h2) {
8566 g_object_unref ( G_OBJECT ( pixmap ) );
8567 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8570 // Reset to background
8571 gdk_draw_drawable (pixmap,
8572 vtl->current_track_newpoint_gc,
8573 vik_viewport_get_pixmap(vvp),
8574 0, 0, 0, 0, -1, -1);
8576 draw_sync_t *passalong;
8579 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
8581 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
8582 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
8583 // thus when we come to reset to the background it would include what we have already drawn!!
8584 gdk_draw_line ( pixmap,
8585 vtl->current_track_newpoint_gc,
8586 x1, y1, event->x, event->y );
8587 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
8589 /* Find out actual distance of current track */
8590 gdouble distance = vik_track_get_length (vtl->current_track);
8592 // Now add distance to where the pointer is //
8595 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
8596 vik_coord_to_latlon ( &coord, &ll );
8597 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
8598 distance = distance + last_step;
8600 // Get elevation data
8601 gdouble elev_gain, elev_loss;
8602 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8604 // Adjust elevation data (if available) for the current pointer position
8606 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
8607 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
8608 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8609 // Adjust elevation of last track point
8610 if ( elev_new > last_tpt->altitude )
8612 elev_gain += elev_new - last_tpt->altitude;
8615 elev_loss += last_tpt->altitude - elev_new;
8620 // Display of the distance 'tooltip' during track creation is controlled by a preference
8622 if ( a_vik_get_create_track_tooltip() ) {
8624 gchar *str = distance_string (distance);
8626 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
8627 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
8628 pango_layout_set_text (pl, str, -1);
8630 pango_layout_get_pixel_size ( pl, &wd, &hd );
8633 // offset from cursor a bit depending on font size
8637 // Create a background block to make the text easier to read over the background map
8638 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
8639 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
8640 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
8642 g_object_unref ( G_OBJECT ( pl ) );
8643 g_object_unref ( G_OBJECT ( background_block_gc ) );
8647 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
8648 passalong->vtl = vtl;
8649 passalong->pixmap = pixmap;
8650 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
8651 passalong->gc = vtl->current_track_newpoint_gc;
8655 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
8657 // Update statusbar with full gain/loss information
8658 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
8660 // draw pixmap when we have time to
8661 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
8662 vtl->draw_sync_done = FALSE;
8663 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
8665 return VIK_LAYER_TOOL_ACK;
8668 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
8670 if ( vtl->current_track && event->keyval == GDK_Escape ) {
8671 vtl->current_track = NULL;
8672 vik_layer_emit_update ( VIK_LAYER(vtl) );
8674 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
8676 if ( vtl->current_track->trackpoints )
8678 GList *last = g_list_last(vtl->current_track->trackpoints);
8679 g_free ( last->data );
8680 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8683 update_statusbar ( vtl );
8685 vik_layer_emit_update ( VIK_LAYER(vtl) );
8692 * Common function to handle trackpoint button requests on either a route or a track
8693 * . enables adding a point via normal click
8694 * . enables removal of last point via right click
8695 * . finishing of the track or route via double clicking
8697 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8701 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8704 if ( event->button == 2 ) {
8705 // As the display is panning, the new track pixmap is now invalid so don't draw it
8706 // otherwise this drawing done results in flickering back to an old image
8707 vtl->draw_sync_do = FALSE;
8711 if ( event->button == 3 )
8713 if ( !vtl->current_track )
8716 if ( vtl->current_track->trackpoints )
8718 GList *last = g_list_last(vtl->current_track->trackpoints);
8719 g_free ( last->data );
8720 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8722 vik_track_calculate_bounds ( vtl->current_track );
8723 update_statusbar ( vtl );
8725 vik_layer_emit_update ( VIK_LAYER(vtl) );
8729 if ( event->type == GDK_2BUTTON_PRESS )
8731 /* subtract last (duplicate from double click) tp then end */
8732 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8734 GList *last = g_list_last(vtl->current_track->trackpoints);
8735 g_free ( last->data );
8736 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8737 /* undo last, then end */
8738 vtl->current_track = NULL;
8740 vik_layer_emit_update ( VIK_LAYER(vtl) );
8744 tp = vik_trackpoint_new();
8745 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8747 /* snap to other TP */
8748 if ( event->state & GDK_CONTROL_MASK )
8750 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8752 tp->coord = other_tp->coord;
8755 tp->newsegment = FALSE;
8756 tp->has_timestamp = FALSE;
8759 if ( vtl->current_track ) {
8760 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
8761 /* Auto attempt to get elevation from DEM data (if it's available) */
8762 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8765 vtl->ct_x1 = vtl->ct_x2;
8766 vtl->ct_y1 = vtl->ct_y2;
8767 vtl->ct_x2 = event->x;
8768 vtl->ct_y2 = event->y;
8770 vik_layer_emit_update ( VIK_LAYER(vtl) );
8774 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8776 // ----------------------------------------------------- if current is a route - switch to new track
8777 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8779 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8780 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8782 new_track_create_common ( vtl, name );
8788 return tool_new_track_or_route_click ( vtl, event, vvp );
8791 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8793 if ( event->button == 2 ) {
8794 // Pan moving ended - enable potential point drawing again
8795 vtl->draw_sync_do = TRUE;
8796 vtl->draw_sync_done = TRUE;
8800 /*** New route ****/
8802 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8807 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8809 // -------------------------- if current is a track - switch to new route
8810 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8812 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8813 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) ) {
8814 new_route_create_common ( vtl, name );
8820 return tool_new_track_or_route_click ( vtl, event, vvp );
8823 /*** New waypoint ****/
8825 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8830 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8833 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8835 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8836 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8837 trw_layer_calculate_bounds_waypoints ( vtl );
8838 vik_layer_emit_update ( VIK_LAYER(vtl) );
8844 /*** Edit trackpoint ****/
8846 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8848 tool_ed_t *t = g_new(tool_ed_t, 1);
8854 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
8859 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8861 tool_ed_t *t = data;
8862 VikViewport *vvp = t->vvp;
8863 TPSearchParams params;
8864 /* OUTDATED DOCUMENTATION:
8865 find 5 pixel range on each side. then put these UTM, and a pointer
8866 to the winning track name (and maybe the winning track itself), and a
8867 pointer to the winning trackpoint, inside an array or struct. pass
8868 this along, do a foreach on the tracks which will do a foreach on the
8871 params.x = event->x;
8872 params.y = event->y;
8873 params.closest_track_id = NULL;
8874 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8875 params.closest_tp = NULL;
8876 params.closest_tpl = NULL;
8877 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8879 if ( event->button != 1 )
8882 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8885 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8888 if ( vtl->current_tpl )
8890 /* 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.) */
8891 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8892 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8897 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8899 if ( current_tr->visible &&
8900 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8901 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8902 marker_begin_move ( t, event->x, event->y );
8908 if ( vtl->tracks_visible )
8909 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8911 if ( params.closest_tp )
8913 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8914 vtl->current_tpl = params.closest_tpl;
8915 vtl->current_tp_id = params.closest_track_id;
8916 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8917 trw_layer_tpwin_init ( vtl );
8918 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8919 vik_layer_emit_update ( VIK_LAYER(vtl) );
8923 if ( vtl->routes_visible )
8924 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8926 if ( params.closest_tp )
8928 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8929 vtl->current_tpl = params.closest_tpl;
8930 vtl->current_tp_id = params.closest_track_id;
8931 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8932 trw_layer_tpwin_init ( vtl );
8933 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8934 vik_layer_emit_update ( VIK_LAYER(vtl) );
8938 /* these aren't the droids you're looking for */
8942 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8944 tool_ed_t *t = data;
8945 VikViewport *vvp = t->vvp;
8947 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8953 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8956 if ( event->state & GDK_CONTROL_MASK )
8958 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8959 if ( tp && tp != vtl->current_tpl->data )
8960 new_coord = tp->coord;
8962 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8965 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8966 marker_moveto ( t, x, y );
8974 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8976 tool_ed_t *t = data;
8977 VikViewport *vvp = t->vvp;
8979 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8981 if ( event->button != 1)
8986 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8989 if ( event->state & GDK_CONTROL_MASK )
8991 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8992 if ( tp && tp != vtl->current_tpl->data )
8993 new_coord = tp->coord;
8996 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8997 if ( vtl->current_tp_track )
8998 vik_track_calculate_bounds ( vtl->current_tp_track );
9000 marker_end_move ( t );
9002 /* diff dist is diff from orig */
9004 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9006 vik_layer_emit_update ( VIK_LAYER(vtl) );
9013 /*** Route Finder ***/
9014 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9019 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9022 if ( !vtl ) return FALSE;
9023 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9024 if ( event->button == 3 && vtl->route_finder_current_track ) {
9026 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9028 vtl->route_finder_coord = *new_end;
9030 vik_layer_emit_update ( VIK_LAYER(vtl) );
9031 /* remove last ' to:...' */
9032 if ( vtl->route_finder_current_track->comment ) {
9033 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9034 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9035 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9036 last_to - vtl->route_finder_current_track->comment - 1);
9037 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9042 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9043 struct LatLon start, end;
9045 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9046 vik_coord_to_latlon ( &(tmp), &end );
9047 vtl->route_finder_coord = tmp; /* for continuations */
9049 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9050 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9051 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9053 vtl->route_finder_check_added_track = TRUE;
9054 vtl->route_finder_started = FALSE;
9057 vik_routing_default_find ( vtl, start, end);
9059 /* see if anything was done -- a track was added or appended to */
9060 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9061 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 ) );
9062 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9063 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9064 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9065 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9068 if ( vtl->route_finder_added_track )
9069 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9071 vtl->route_finder_added_track = NULL;
9072 vtl->route_finder_check_added_track = FALSE;
9073 vtl->route_finder_append = FALSE;
9075 vik_layer_emit_update ( VIK_LAYER(vtl) );
9077 vtl->route_finder_started = TRUE;
9078 vtl->route_finder_coord = tmp;
9079 vtl->route_finder_current_track = NULL;
9084 /*** Show picture ****/
9086 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9091 /* Params are: vvp, event, last match found or NULL */
9092 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9094 if ( wp->image && wp->visible )
9096 gint x, y, slackx, slacky;
9097 GdkEventButton *event = (GdkEventButton *) params[1];
9099 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9100 slackx = wp->image_width / 2;
9101 slacky = wp->image_height / 2;
9102 if ( x <= event->x + slackx && x >= event->x - slackx
9103 && y <= event->y + slacky && y >= event->y - slacky )
9105 params[2] = wp->image; /* we've found a match. however continue searching
9106 * since we want to find the last match -- that
9107 * is, the match that was drawn last. */
9112 static void trw_layer_show_picture ( gpointer pass_along[6] )
9114 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9116 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9119 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9120 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9121 g_free ( quoted_file );
9122 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9124 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() );
9125 g_error_free ( err );
9128 #endif /* WINDOWS */
9131 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9133 gpointer params[3] = { vvp, event, NULL };
9134 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9136 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9139 static gpointer pass_along[6];
9140 pass_along[0] = vtl;
9141 pass_along[5] = params[2];
9142 trw_layer_show_picture ( pass_along );
9143 return TRUE; /* found a match */
9146 return FALSE; /* go through other layers, searching for a match */
9149 /***************************************************************************
9151 ***************************************************************************/
9154 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9156 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9157 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9160 /* Structure for thumbnail creating data used in the background thread */
9162 VikTrwLayer *vtl; // Layer needed for redrawing
9163 GSList *pics; // Image list
9164 } thumbnail_create_thread_data;
9166 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9168 guint total = g_slist_length(tctd->pics), done = 0;
9169 while ( tctd->pics )
9171 a_thumbnails_create ( (gchar *) tctd->pics->data );
9172 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9174 return -1; /* Abort thread */
9176 tctd->pics = tctd->pics->next;
9179 // Redraw to show the thumbnails as they are now created
9180 if ( IS_VIK_LAYER(tctd->vtl) )
9181 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9186 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9188 while ( tctd->pics )
9190 g_free ( tctd->pics->data );
9191 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9196 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9198 if ( ! vtl->has_verified_thumbnails )
9200 GSList *pics = NULL;
9201 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9204 gint len = g_slist_length ( pics );
9205 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9206 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9209 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9211 (vik_thr_func) create_thumbnails_thread,
9213 (vik_thr_free_func) thumbnail_create_thread_free,
9221 static const gchar* my_track_colors ( gint ii )
9223 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9235 // Fast and reliable way of returning a colour
9236 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9239 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9241 GHashTableIter iter;
9242 gpointer key, value;
9246 g_hash_table_iter_init ( &iter, vtl->tracks );
9248 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9250 // Tracks get a random spread of colours if not already assigned
9251 if ( ! VIK_TRACK(value)->has_color ) {
9252 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9253 VIK_TRACK(value)->color = vtl->track_color;
9255 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9257 VIK_TRACK(value)->has_color = TRUE;
9260 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9263 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9269 g_hash_table_iter_init ( &iter, vtl->routes );
9271 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9273 // Routes get an intermix of reds
9274 if ( ! VIK_TRACK(value)->has_color ) {
9276 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9278 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9279 VIK_TRACK(value)->has_color = TRUE;
9282 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9289 * (Re)Calculate the bounds of the waypoints in this layer,
9290 * This should be called whenever waypoints are changed
9292 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9294 struct LatLon topleft = { 0.0, 0.0 };
9295 struct LatLon bottomright = { 0.0, 0.0 };
9298 GHashTableIter iter;
9299 gpointer key, value;
9301 g_hash_table_iter_init ( &iter, vtl->waypoints );
9303 // Set bounds to first point
9304 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9305 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9306 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9309 // Ensure there is another point...
9310 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9312 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9314 // See if this point increases the bounds.
9315 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9317 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9318 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9319 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9320 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9324 vtl->waypoints_bbox.north = topleft.lat;
9325 vtl->waypoints_bbox.east = bottomright.lon;
9326 vtl->waypoints_bbox.south = bottomright.lat;
9327 vtl->waypoints_bbox.west = topleft.lon;
9330 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9332 vik_track_calculate_bounds ( trk );
9335 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9337 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9338 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9341 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9343 if ( ! VIK_LAYER(vtl)->vt )
9346 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9347 if ( g_hash_table_size (vtl->tracks) > 1 )
9348 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9350 if ( g_hash_table_size (vtl->routes) > 1 )
9351 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9353 if ( g_hash_table_size (vtl->waypoints) > 1 )
9354 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9357 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9359 trw_layer_verify_thumbnails ( vtl, vvp );
9360 trw_layer_track_alloc_colors ( vtl );
9362 trw_layer_calculate_bounds_waypoints ( vtl );
9363 trw_layer_calculate_bounds_tracks ( vtl );
9365 // Apply treeview sort after loading all the tracks for this layer
9366 // (rather than sorted insert on each individual track additional)
9367 // and after subsequent changes to the properties as the specified order may have changed.
9368 // since the sorting of a treeview section is now very quick
9369 // NB sorting is also performed after every name change as well to maintain the list order
9370 trw_layer_sort_all ( vtl );
9373 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9375 return vtl->coord_mode;
9379 * Uniquify the whole layer
9380 * Also requires the layers panel as the names shown there need updating too
9381 * Returns whether the operation was successful or not
9383 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9386 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9387 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9388 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9394 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9396 vik_coord_convert ( &(wp->coord), *dest_mode );
9399 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9401 vik_track_convert ( tr, *dest_mode );
9404 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9406 if ( vtl->coord_mode != dest_mode )
9408 vtl->coord_mode = dest_mode;
9409 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9410 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9411 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9415 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9417 vtl->menu_selection = selection;
9420 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9422 return (vtl->menu_selection);
9425 /* ----------- Downloading maps along tracks --------------- */
9427 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9429 /* TODO: calculating based on current size of viewport */
9430 const gdouble w_at_zoom_0_125 = 0.0013;
9431 const gdouble h_at_zoom_0_125 = 0.0011;
9432 gdouble zoom_factor = zoom_level/0.125;
9434 wh->lat = h_at_zoom_0_125 * zoom_factor;
9435 wh->lon = w_at_zoom_0_125 * zoom_factor;
9437 return 0; /* all OK */
9440 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9442 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9443 (dist->lat >= ABS(to->north_south - from->north_south)))
9446 VikCoord *coord = g_malloc(sizeof(VikCoord));
9447 coord->mode = VIK_COORD_LATLON;
9449 if (ABS(gradient) < 1) {
9450 if (from->east_west > to->east_west)
9451 coord->east_west = from->east_west - dist->lon;
9453 coord->east_west = from->east_west + dist->lon;
9454 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9456 if (from->north_south > to->north_south)
9457 coord->north_south = from->north_south - dist->lat;
9459 coord->north_south = from->north_south + dist->lat;
9460 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9466 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9468 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9469 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9471 VikCoord *next = from;
9473 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9475 list = g_list_prepend(list, next);
9481 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9483 typedef struct _Rect {
9488 #define GLRECT(iter) ((Rect *)((iter)->data))
9491 GList *rects_to_download = NULL;
9494 if (get_download_area_width(vvp, zoom_level, &wh))
9497 GList *iter = tr->trackpoints;
9501 gboolean new_map = TRUE;
9502 VikCoord *cur_coord, tl, br;
9505 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
9507 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9508 rect = g_malloc(sizeof(Rect));
9511 rect->center = *cur_coord;
9512 rects_to_download = g_list_prepend(rects_to_download, rect);
9517 gboolean found = FALSE;
9518 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9519 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
9530 GList *fillins = NULL;
9531 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
9532 /* seems that ATM the function get_next_coord works only for LATLON */
9533 if ( cur_coord->mode == VIK_COORD_LATLON ) {
9534 /* fill-ins for far apart points */
9535 GList *cur_rect, *next_rect;
9536 for (cur_rect = rects_to_download;
9537 (next_rect = cur_rect->next) != NULL;
9538 cur_rect = cur_rect->next) {
9539 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
9540 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
9541 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
9545 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
9548 GList *iter = fillins;
9550 cur_coord = (VikCoord *)(iter->data);
9551 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9552 rect = g_malloc(sizeof(Rect));
9555 rect->center = *cur_coord;
9556 rects_to_download = g_list_prepend(rects_to_download, rect);
9561 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9562 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
9566 for (iter = fillins; iter; iter = iter->next)
9568 g_list_free(fillins);
9570 if (rects_to_download) {
9571 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
9572 g_free(rect_iter->data);
9573 g_list_free(rects_to_download);
9577 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
9580 gint selected_map, default_map;
9581 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
9582 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
9583 gint selected_zoom, default_zoom;
9587 VikTrwLayer *vtl = pass_along[0];
9588 VikLayersPanel *vlp = pass_along[1];
9590 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
9591 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
9593 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
9597 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9599 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
9600 int num_maps = g_list_length(vmls);
9603 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
9607 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
9608 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
9610 gchar **np = map_names;
9611 VikMapsLayer **lp = map_layers;
9612 for (i = 0; i < num_maps; i++) {
9613 gboolean dup = FALSE;
9614 vml = (VikMapsLayer *)(vmls->data);
9615 for (j = 0; j < i; j++) { /* no duplicate allowed */
9616 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
9623 *np++ = vik_maps_layer_get_map_label(vml);
9629 num_maps = lp - map_layers;
9631 for (default_map = 0; default_map < num_maps; default_map++) {
9632 /* TODO: check for parent layer's visibility */
9633 if (VIK_LAYER(map_layers[default_map])->visible)
9636 default_map = (default_map == num_maps) ? 0 : default_map;
9638 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
9639 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
9640 if (cur_zoom == zoom_vals[default_zoom])
9643 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
9645 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
9648 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
9651 for (i = 0; i < num_maps; i++)
9652 g_free(map_names[i]);
9660 /**** lowest waypoint number calculation ***/
9661 static gint highest_wp_number_name_to_number(const gchar *name) {
9662 if ( strlen(name) == 3 ) {
9664 if ( n < 100 && name[0] != '0' )
9666 if ( n < 10 && name[0] != '0' )
9674 static void highest_wp_number_reset(VikTrwLayer *vtl)
9676 vtl->highest_wp_number = -1;
9679 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
9681 /* if is bigger that top, add it */
9682 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
9683 if ( new_wp_num > vtl->highest_wp_number )
9684 vtl->highest_wp_number = new_wp_num;
9687 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
9689 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
9690 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
9691 if ( vtl->highest_wp_number == old_wp_num ) {
9693 vtl->highest_wp_number--;
9695 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9696 /* search down until we find something that *does* exist */
9698 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
9699 vtl->highest_wp_number--;
9700 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9705 /* get lowest unused number */
9706 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
9709 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
9711 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
9712 return g_strdup(buf);