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>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
26 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
33 #include "vikmapslayer.h"
34 #include "vikgpslayer.h"
35 #include "viktrwlayer_tpwin.h"
36 #include "viktrwlayer_propwin.h"
37 #ifdef VIK_CONFIG_GEOTAG
38 #include "viktrwlayer_geotag.h"
39 #include "geotag_exif.h"
41 #include "garminsymbols.h"
42 #include "thumbnails.h"
43 #include "background.h"
48 #include "geonamessearch.h"
49 #ifdef VIK_CONFIG_OPENSTREETMAP
50 #include "osm-traces.h"
53 #include "datasources.h"
54 #include "datasource_gps.h"
55 #include "vikexttool_datasources.h"
58 #include "icons/icons.h"
72 #include <gdk/gdkkeysyms.h>
74 #include <glib/gstdio.h>
75 #include <glib/gi18n.h>
77 #ifdef VIK_CONFIG_GOOGLE
78 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
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;
136 guint8 drawpoints_size;
137 guint8 drawelevation;
138 guint8 elevation_factor;
142 guint8 drawdirections;
143 guint8 drawdirections_size;
144 guint8 line_thickness;
145 guint8 bg_line_thickness;
149 gboolean wp_draw_symbols;
150 font_size_t wp_font_size;
153 gdouble track_draw_speed_factor;
155 GdkGC *track_1color_gc;
156 GdkColor track_color;
157 GdkGC *current_track_gc;
158 // Separate GC for a track's potential new point as drawn via separate method
159 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
160 GdkGC *current_track_newpoint_gc;
161 GdkGC *track_bg_gc; GdkColor track_bg_color;
162 GdkGC *waypoint_gc; GdkColor waypoint_color;
163 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
164 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
166 GdkFont *waypoint_font;
167 VikTrack *current_track; // ATM shared between new tracks and new routes
168 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
169 gboolean draw_sync_done;
170 gboolean draw_sync_do;
172 VikCoordMode coord_mode;
174 /* wp editing tool */
175 VikWaypoint *current_wp;
176 gpointer current_wp_id;
178 gboolean waypoint_rightclick;
180 /* track editing tool */
182 VikTrack *current_tp_track;
183 gpointer current_tp_id;
184 VikTrwLayerTpwin *tpwin;
186 /* track editing tool -- more specifically, moving tps */
189 /* route finder tool */
190 gboolean route_finder_started;
191 VikCoord route_finder_coord;
192 gboolean route_finder_check_added_track;
193 VikTrack *route_finder_added_track;
194 VikTrack *route_finder_current_track;
195 gboolean route_finder_append;
202 guint16 image_cache_size;
204 /* for waypoint text */
205 PangoLayout *wplabellayout;
207 gboolean has_verified_thumbnails;
209 GtkMenu *wp_right_click_menu;
210 GtkMenu *track_right_click_menu;
213 VikStdLayerMenuItem menu_selection;
215 gint highest_wp_number;
218 /* A caached waypoint image. */
221 gchar *image; /* filename */
224 struct DrawingParams {
229 guint16 width, height;
230 gdouble cc; // Cosine factor in track directions
231 gdouble ss; // Sine factor in track directions
232 const VikCoord *center;
233 gboolean one_zone, lat_lon;
234 gdouble ce1, ce2, cn1, cn2;
238 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
240 static void trw_layer_delete_item ( gpointer pass_along[6] );
241 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
242 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
244 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] );
245 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
246 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
248 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
249 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
251 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
252 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
254 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl );
256 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
257 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
258 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
259 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
260 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
261 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
262 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
263 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
264 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
265 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
266 static void trw_layer_append_track ( gpointer pass_along[6] );
267 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
268 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
269 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
270 static void trw_layer_split_segments ( gpointer pass_along[6] );
271 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
272 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
273 static void trw_layer_reverse ( gpointer pass_along[6] );
274 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
275 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
276 static void trw_layer_show_picture ( gpointer pass_along[6] );
277 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
279 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
280 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
281 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
282 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
283 static void trw_layer_new_wp ( gpointer lav[2] );
284 static void trw_layer_new_track ( gpointer lav[2] );
285 static void trw_layer_new_route ( gpointer lav[2] );
286 static void trw_layer_finish_track ( gpointer lav[2] );
287 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
288 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
289 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
290 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
291 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
292 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
293 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
294 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
295 #ifdef VIK_CONFIG_GEOTAG
296 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
297 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
298 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
299 static void trw_layer_geotagging ( gpointer lav[2] );
301 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
302 #ifdef VIK_CONFIG_GOOGLE
303 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
305 #ifdef VIK_CONFIG_OPENSTREETMAP
306 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
307 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
309 #ifdef VIK_CONFIG_GEOCACHES
310 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
312 #ifdef VIK_CONFIG_GEOTAG
313 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
315 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
316 static void trw_layer_gps_upload ( gpointer lav[2] );
318 // Specific route versions:
319 // Most track handling functions can handle operating on the route list
320 // However these ones are easier in separate functions
321 static void trw_layer_auto_routes_view ( gpointer lav[2] );
322 static void trw_layer_delete_all_routes ( gpointer lav[2] );
323 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
326 static void trw_layer_properties_item ( gpointer pass_along[7] );
327 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
328 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
329 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
331 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
332 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
333 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
335 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
336 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
337 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
338 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
340 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
341 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
342 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
343 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
344 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
345 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
346 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
347 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
348 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
349 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
350 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
351 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
352 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
353 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
354 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
355 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
356 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
357 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
358 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
359 #ifdef VIK_CONFIG_GOOGLE
360 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
361 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
364 static void cached_pixbuf_free ( CachedPixbuf *cp );
365 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
367 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
368 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
370 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
371 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
373 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
374 static void highest_wp_number_reset(VikTrwLayer *vtl);
375 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
376 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
378 // Note for the following tool GtkRadioActionEntry texts:
379 // the very first text value is an internal name not displayed anywhere
380 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
381 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
382 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
383 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
384 static VikToolInterface trw_layer_tools[] = {
385 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
386 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
387 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
389 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
391 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
392 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
393 (VikToolMouseFunc) tool_new_track_click,
394 (VikToolMouseMoveFunc) tool_new_track_move,
395 (VikToolMouseFunc) tool_new_track_release,
396 (VikToolKeyFunc) tool_new_track_key_press,
397 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
398 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
400 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
401 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
402 (VikToolMouseFunc) tool_new_route_click,
403 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
404 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
405 (VikToolKeyFunc) tool_new_track_key_press, // -/#
406 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
407 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
409 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
410 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
411 (VikToolMouseFunc) tool_edit_waypoint_click,
412 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
413 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
415 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
417 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
418 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
419 (VikToolMouseFunc) tool_edit_trackpoint_click,
420 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
421 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
423 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
425 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
426 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
427 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
429 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
431 #ifdef VIK_CONFIG_GOOGLE
432 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
433 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
434 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
436 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
441 TOOL_CREATE_WAYPOINT=0,
445 TOOL_EDIT_TRACKPOINT,
447 #ifdef VIK_CONFIG_GOOGLE
453 /****** PARAMETERS ******/
455 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
456 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
458 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
459 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
461 #define MIN_POINT_SIZE 2
462 #define MAX_POINT_SIZE 10
464 #define MIN_ARROW_SIZE 3
465 #define MAX_ARROW_SIZE 20
467 static VikLayerParamScale params_scales[] = {
468 /* min max step digits */
469 { 1, 10, 1, 0 }, /* line_thickness */
470 { 0, 100, 1, 0 }, /* track draw speed factor */
471 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
472 /* 5 * step == how much to turn */
473 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
474 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
475 { 5, 500, 5, 0 }, // 5: image cache_size - " "
476 { 0, 8, 1, 0 }, // 6: Background line thickness
477 { 1, 64, 1, 0 }, /* wpsize */
478 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
479 { 1, 100, 1, 0 }, // 9: elevation factor
480 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
481 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
484 static gchar* params_font_sizes[] = {
485 N_("Extra Extra Small"),
491 N_("Extra Extra Large"),
494 static VikLayerParamData black_color_default ( void ) {
495 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
497 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
498 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
499 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
500 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
501 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
502 static VikLayerParamData trackbgcolor_default ( void ) {
503 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
505 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
506 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
507 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
509 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
510 static VikLayerParamData wptextcolor_default ( void ) {
511 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
513 static VikLayerParamData wpbgcolor_default ( void ) {
514 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
516 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
517 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
519 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
520 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
521 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
523 VikLayerParam trw_layer_params[] = {
524 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
525 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
526 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
528 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default },
529 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
530 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default },
531 { 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 },
532 { 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 },
533 { 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 },
534 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL, trkdirectionsize_default },
535 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default },
536 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL, trkpointsize_default },
537 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default },
538 { 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 },
540 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
541 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 },
542 { 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 },
544 { 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 },
545 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default },
546 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
547 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default },
549 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default },
550 { 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 },
551 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default },
552 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default },
553 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default },
554 { 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 },
555 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default },
556 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[7], NULL, NULL, wpsize_default },
557 { 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 },
559 { 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 },
560 { 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 },
561 { 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 },
562 { 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 },
565 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
567 // Sublayer visibilities
606 *** 1) Add to trw_layer_params and enumeration
607 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
610 /****** END PARAMETERS ******/
612 /* Layer Interface function definitions */
613 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
614 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
615 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
616 static void trw_layer_free ( VikTrwLayer *trwlayer );
617 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
618 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
619 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
620 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
621 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
622 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
623 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
624 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
625 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
626 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
627 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
628 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
629 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
630 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
631 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
632 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
633 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
634 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
635 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
636 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
637 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
638 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
639 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
640 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
641 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
642 /* End Layer Interface function definitions */
644 VikLayerInterface vik_trw_layer_interface = {
651 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
655 params_groups, /* params_groups */
656 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
660 (VikLayerFuncCreate) trw_layer_create,
661 (VikLayerFuncRealize) trw_layer_realize,
662 (VikLayerFuncPostRead) trw_layer_post_read,
663 (VikLayerFuncFree) trw_layer_free,
665 (VikLayerFuncProperties) NULL,
666 (VikLayerFuncDraw) trw_layer_draw,
667 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
669 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
670 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
672 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
673 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
675 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
676 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
677 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
678 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
679 (VikLayerFuncLayerSelected) trw_layer_selected,
681 (VikLayerFuncMarshall) trw_layer_marshall,
682 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
684 (VikLayerFuncSetParam) trw_layer_set_param,
685 (VikLayerFuncGetParam) trw_layer_get_param,
687 (VikLayerFuncReadFileData) a_gpspoint_read_file,
688 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
690 (VikLayerFuncDeleteItem) trw_layer_del_item,
691 (VikLayerFuncCutItem) trw_layer_cut_item,
692 (VikLayerFuncCopyItem) trw_layer_copy_item,
693 (VikLayerFuncPasteItem) trw_layer_paste_item,
694 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
696 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
698 (VikLayerFuncSelectClick) trw_layer_select_click,
699 (VikLayerFuncSelectMove) trw_layer_select_move,
700 (VikLayerFuncSelectRelease) trw_layer_select_release,
701 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
704 GType vik_trw_layer_get_type ()
706 static GType vtl_type = 0;
710 static const GTypeInfo vtl_info =
712 sizeof (VikTrwLayerClass),
713 NULL, /* base_init */
714 NULL, /* base_finalize */
715 NULL, /* class init */
716 NULL, /* class_finalize */
717 NULL, /* class_data */
718 sizeof (VikTrwLayer),
720 NULL /* instance init */
722 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
728 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
730 static gpointer pass_along[6];
736 pass_along[1] = NULL;
737 pass_along[2] = GINT_TO_POINTER (subtype);
738 pass_along[3] = sublayer;
739 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
740 pass_along[5] = NULL;
742 trw_layer_delete_item ( pass_along );
745 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
747 static gpointer pass_along[6];
753 pass_along[1] = NULL;
754 pass_along[2] = GINT_TO_POINTER (subtype);
755 pass_along[3] = sublayer;
756 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
757 pass_along[5] = NULL;
759 trw_layer_copy_item_cb(pass_along);
760 trw_layer_cut_item_cb(pass_along);
763 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
765 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
766 gint subtype = GPOINTER_TO_INT (pass_along[2]);
767 gpointer * sublayer = pass_along[3];
771 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
775 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
776 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
777 if ( wp && wp->name )
780 name = NULL; // Broken :(
782 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
783 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
784 if ( trk && trk->name )
787 name = NULL; // Broken :(
790 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
791 if ( trk && trk->name )
794 name = NULL; // Broken :(
797 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
798 subtype, len, name, data);
802 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
804 trw_layer_copy_item_cb(pass_along);
805 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
806 trw_layer_delete_item(pass_along);
809 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
811 // Slightly cheating method, routing via the panels capability
812 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
815 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
825 GByteArray *ba = g_byte_array_new ();
827 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
828 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
829 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
830 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
832 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
835 g_byte_array_append ( ba, id, il );
843 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
850 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
854 w = vik_waypoint_unmarshall ( item, len );
855 // When copying - we'll create a new name based on the original
856 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
857 vik_trw_layer_add_waypoint ( vtl, name, w );
858 waypoint_convert (NULL, w, &vtl->coord_mode);
860 trw_layer_calculate_bounds_waypoints ( vtl );
862 // Consider if redraw necessary for the new item
863 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
864 vik_layer_emit_update ( VIK_LAYER(vtl) );
867 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
871 t = vik_track_unmarshall ( item, len );
872 // When copying - we'll create a new name based on the original
873 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
874 vik_trw_layer_add_track ( vtl, name, t );
875 vik_track_convert (t, vtl->coord_mode);
877 // Consider if redraw necessary for the new item
878 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
879 vik_layer_emit_update ( VIK_LAYER(vtl) );
882 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
886 t = vik_track_unmarshall ( item, len );
887 // When copying - we'll create a new name based on the original
888 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
889 vik_trw_layer_add_route ( vtl, name, t );
890 vik_track_convert (t, vtl->coord_mode);
892 // Consider if redraw necessary for the new item
893 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
894 vik_layer_emit_update ( VIK_LAYER(vtl) );
900 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
907 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
911 case PARAM_TV: vtl->tracks_visible = data.b; break;
912 case PARAM_WV: vtl->waypoints_visible = data.b; break;
913 case PARAM_RV: vtl->routes_visible = data.b; break;
914 case PARAM_DM: vtl->drawmode = data.u; break;
916 vtl->track_color = data.c;
917 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
919 case PARAM_DP: vtl->drawpoints = data.b; break;
921 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
922 vtl->drawpoints_size = data.u;
924 case PARAM_DE: vtl->drawelevation = data.b; break;
925 case PARAM_DS: vtl->drawstops = data.b; break;
926 case PARAM_DL: vtl->drawlines = data.b; break;
927 case PARAM_DD: vtl->drawdirections = data.b; break;
929 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
930 vtl->drawdirections_size = data.u;
932 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
933 vtl->stop_length = data.u;
935 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
936 vtl->elevation_factor = data.u;
938 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
940 vtl->line_thickness = data.u;
941 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
944 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
946 vtl->bg_line_thickness = data.u;
947 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
951 vtl->track_bg_color = data.c;
952 if ( vtl->track_bg_gc )
953 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
955 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
956 case PARAM_DLA: vtl->drawlabels = data.b; break;
957 case PARAM_DI: vtl->drawimages = data.b; break;
958 case PARAM_IS: if ( data.u != vtl->image_size )
960 vtl->image_size = data.u;
961 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
962 g_queue_free ( vtl->image_cache );
963 vtl->image_cache = g_queue_new ();
966 case PARAM_IA: vtl->image_alpha = data.u; break;
967 case PARAM_ICS: vtl->image_cache_size = data.u;
968 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
969 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
972 vtl->waypoint_color = data.c;
973 if ( vtl->waypoint_gc )
974 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
977 vtl->waypoint_text_color = data.c;
978 if ( vtl->waypoint_text_gc )
979 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
982 vtl->waypoint_bg_color = data.c;
983 if ( vtl->waypoint_bg_gc )
984 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
987 vtl->wpbgand = data.b;
988 if ( vtl->waypoint_bg_gc )
989 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
991 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
992 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
993 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
994 case PARAM_WPFONTSIZE:
995 if ( data.u < FS_NUM_SIZES ) {
996 vtl->wp_font_size = data.u;
997 g_free ( vtl->wp_fsize_str );
998 switch ( vtl->wp_font_size ) {
999 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1000 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1001 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1002 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1003 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1004 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1005 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1013 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1015 VikLayerParamData rv;
1018 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1019 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1020 case PARAM_RV: rv.b = vtl->routes_visible; break;
1021 case PARAM_DM: rv.u = vtl->drawmode; break;
1022 case PARAM_TC: rv.c = vtl->track_color; break;
1023 case PARAM_DP: rv.b = vtl->drawpoints; break;
1024 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1025 case PARAM_DE: rv.b = vtl->drawelevation; break;
1026 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1027 case PARAM_DS: rv.b = vtl->drawstops; break;
1028 case PARAM_SL: rv.u = vtl->stop_length; break;
1029 case PARAM_DL: rv.b = vtl->drawlines; break;
1030 case PARAM_DD: rv.b = vtl->drawdirections; break;
1031 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1032 case PARAM_LT: rv.u = vtl->line_thickness; break;
1033 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1034 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1035 case PARAM_DI: rv.b = vtl->drawimages; break;
1036 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1037 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1038 case PARAM_IS: rv.u = vtl->image_size; break;
1039 case PARAM_IA: rv.u = vtl->image_alpha; break;
1040 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1041 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1042 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1043 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1044 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1045 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1046 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1047 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1048 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1053 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1060 // Use byte arrays to store sublayer data
1061 // much like done elsewhere e.g. vik_layer_marshall_params()
1062 GByteArray *ba = g_byte_array_new ( );
1067 guint object_length;
1070 // the length of the item
1071 // the sublayer type of item
1072 // the the actual item
1073 #define tlm_append(object_pointer, size, type) \
1075 object_length = (size); \
1076 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1077 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1078 g_byte_array_append ( ba, (object_pointer), object_length );
1080 // Layer parameters first
1081 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1082 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1083 g_byte_array_append ( ba, pd, pl );
1086 // Now sublayer data
1087 GHashTableIter iter;
1088 gpointer key, value;
1091 g_hash_table_iter_init ( &iter, vtl->waypoints );
1092 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1093 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1094 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1099 g_hash_table_iter_init ( &iter, vtl->tracks );
1100 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1101 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1102 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1107 g_hash_table_iter_init ( &iter, vtl->routes );
1108 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1109 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1110 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1120 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1122 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1124 gint consumed_length;
1126 // First the overall layer parameters
1127 memcpy(&pl, data, sizeof(pl));
1129 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1132 consumed_length = pl;
1133 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1135 #define tlm_size (*(gint *)data)
1136 // See marshalling above for order of how this is written
1138 data += sizeof_len_and_subtype + tlm_size;
1140 // Now the individual sublayers:
1142 while ( *data && consumed_length < len ) {
1143 // Normally four extra bytes at the end of the datastream
1144 // (since it's a GByteArray and that's where it's length is stored)
1145 // So only attempt read when there's an actual block of sublayer data
1146 if ( consumed_length + tlm_size < len ) {
1148 // Reuse pl to read the subtype from the data stream
1149 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1151 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1152 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1153 gchar *name = g_strdup ( trk->name );
1154 vik_trw_layer_add_track ( vtl, name, trk );
1157 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1158 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1159 gchar *name = g_strdup ( wp->name );
1160 vik_trw_layer_add_waypoint ( vtl, name, wp );
1163 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1164 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1165 gchar *name = g_strdup ( trk->name );
1166 vik_trw_layer_add_route ( vtl, name, trk );
1170 consumed_length += tlm_size + sizeof_len_and_subtype;
1173 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1175 // Not stored anywhere else so need to regenerate
1176 trw_layer_calculate_bounds_waypoints ( vtl );
1181 // Keep interesting hash function at least visible
1183 static guint strcase_hash(gconstpointer v)
1185 // 31 bit hash function
1188 gchar s[128]; // malloc is too slow for reading big files
1191 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1192 p[i] = toupper(t[i]);
1198 for (p += 1; *p != '\0'; p++)
1199 h = (h << 5) - h + *p;
1206 // Stick a 1 at the end of the function name to make it more unique
1207 // thus more easily searchable in a simple text editor
1208 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1210 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1211 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1213 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1214 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1216 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1217 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1218 // and with normal PC processing capabilities - it has negligibile performance impact
1219 // This also minimized the amount of rework - as the management of the hash tables already exists.
1221 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1222 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1223 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1225 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1226 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1227 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1228 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1229 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1230 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1232 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1234 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1236 // Param settings that are not available via the GUI
1237 // Force to on after processing params (which defaults them to off with a zero value)
1238 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1240 rv->draw_sync_done = TRUE;
1241 rv->draw_sync_do = TRUE;
1247 static void trw_layer_free ( VikTrwLayer *trwlayer )
1249 g_hash_table_destroy(trwlayer->waypoints);
1250 g_hash_table_destroy(trwlayer->tracks);
1252 /* ODC: replace with GArray */
1253 trw_layer_free_track_gcs ( trwlayer );
1255 if ( trwlayer->wp_right_click_menu )
1256 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1258 if ( trwlayer->track_right_click_menu )
1259 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1261 if ( trwlayer->wplabellayout != NULL)
1262 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1264 if ( trwlayer->waypoint_gc != NULL )
1265 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1267 if ( trwlayer->waypoint_text_gc != NULL )
1268 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1270 if ( trwlayer->waypoint_bg_gc != NULL )
1271 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1273 g_free ( trwlayer->wp_fsize_str );
1275 if ( trwlayer->tpwin != NULL )
1276 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1278 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1279 g_queue_free ( trwlayer->image_cache );
1282 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1286 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1287 dp->xmpp = vik_viewport_get_xmpp ( vp );
1288 dp->ympp = vik_viewport_get_ympp ( vp );
1289 dp->width = vik_viewport_get_width ( vp );
1290 dp->height = vik_viewport_get_height ( vp );
1291 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1292 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1294 dp->center = vik_viewport_get_center ( vp );
1295 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1296 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1301 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1302 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1303 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1305 dp->ce1 = dp->center->east_west-w2;
1306 dp->ce2 = dp->center->east_west+w2;
1307 dp->cn1 = dp->center->north_south-h2;
1308 dp->cn2 = dp->center->north_south+h2;
1309 } else if ( dp->lat_lon ) {
1310 VikCoord upperleft, bottomright;
1311 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1312 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1313 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1314 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1315 dp->ce1 = upperleft.east_west;
1316 dp->ce2 = bottomright.east_west;
1317 dp->cn1 = bottomright.north_south;
1318 dp->cn2 = upperleft.north_south;
1321 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1325 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1326 * Here a simple traffic like light colour system is used:
1327 * . slow points are red
1328 * . average is yellow
1329 * . fast points are green
1331 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1334 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1335 if ( average_speed > 0 ) {
1336 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1337 if ( rv < low_speed )
1338 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1339 else if ( rv > high_speed )
1340 return VIK_TRW_LAYER_TRACK_GC_FAST;
1342 return VIK_TRW_LAYER_TRACK_GC_AVER;
1345 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1348 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1350 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1351 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1352 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1353 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1356 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1358 if ( ! track->visible )
1361 /* TODO: this function is a mess, get rid of any redundancy */
1362 GList *list = track->trackpoints;
1364 gboolean useoldvals = TRUE;
1366 gboolean drawpoints;
1368 gboolean drawelevation;
1369 gdouble min_alt, max_alt, alt_diff = 0;
1371 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1372 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1375 if ( dp->vtl->drawelevation )
1377 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1378 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1379 alt_diff = max_alt - min_alt;
1382 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1383 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1384 trw_layer_draw_track ( id, track, dp, TRUE );
1386 if ( draw_track_outline )
1387 drawpoints = drawstops = FALSE;
1389 drawpoints = dp->vtl->drawpoints;
1390 drawstops = dp->vtl->drawstops;
1393 gboolean drawing_highlight = FALSE;
1394 /* Current track - used for creation */
1395 if ( track == dp->vtl->current_track )
1396 main_gc = dp->vtl->current_track_gc;
1398 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1399 /* Draw all tracks of the layer in special colour */
1400 /* if track is member of selected layer or is the current selected track
1401 then draw in the highlight colour.
1402 NB this supercedes the drawmode */
1403 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1404 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1405 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1406 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1407 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1408 drawing_highlight = TRUE;
1411 if ( !drawing_highlight ) {
1412 // Still need to figure out the gc according to the drawing mode:
1413 switch ( dp->vtl->drawmode ) {
1414 case DRAWMODE_BY_TRACK:
1415 if ( dp->vtl->track_1color_gc )
1416 g_object_unref ( dp->vtl->track_1color_gc );
1417 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1418 main_gc = dp->vtl->track_1color_gc;
1421 // Mostly for DRAWMODE_ALL_SAME_COLOR
1422 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1423 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1430 int x, y, oldx, oldy;
1431 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1433 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1435 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1437 // Draw the first point as something a bit different from the normal points
1438 // ATM it's slightly bigger and a triangle
1440 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1441 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1447 gdouble average_speed = 0.0;
1448 gdouble low_speed = 0.0;
1449 gdouble high_speed = 0.0;
1450 // If necessary calculate these values - which is done only once per track redraw
1451 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1452 // the percentage factor away from the average speed determines transistions between the levels
1453 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1454 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1455 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1458 while ((list = g_list_next(list)))
1460 tp = VIK_TRACKPOINT(list->data);
1461 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1463 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1464 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1465 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1466 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1467 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1469 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1472 * If points are the same in display coordinates, don't draw.
1474 if ( useoldvals && x == oldx && y == oldy )
1476 // Still need to process points to ensure 'stops' are drawn if required
1477 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1478 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1479 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 );
1484 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1485 if ( drawpoints || dp->vtl->drawlines ) {
1486 // setup main_gc for both point and line drawing
1487 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1488 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 ) );
1492 if ( drawpoints && ! draw_track_outline )
1497 * The concept of drawing stops is that a trackpoint
1498 * that is if the next trackpoint has a timestamp far into
1499 * the future, we draw a circle of 6x trackpoint size,
1500 * instead of a rectangle of 2x trackpoint size.
1501 * This is drawn first so the trackpoint will be drawn on top
1504 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1505 /* Stop point. Draw 6x circle. Always in redish colour */
1506 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 );
1508 /* Regular point - draw 2x square. */
1509 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1512 /* Final point - draw 4x circle. */
1513 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 );
1516 if ((!tp->newsegment) && (dp->vtl->drawlines))
1519 /* UTM only: zone check */
1520 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1521 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1524 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1526 if ( draw_track_outline ) {
1527 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1531 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1533 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1535 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1540 tmp[1].y = oldy-FIXALTITUDE(list->data);
1542 tmp[2].y = y-FIXALTITUDE(list->next->data);
1547 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1548 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1550 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1551 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1553 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1558 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1559 // Draw an arrow at the mid point to show the direction of the track
1560 // Code is a rework from vikwindow::draw_ruler()
1561 gint midx = (oldx + x) / 2;
1562 gint midy = (oldy + y) / 2;
1564 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1565 // Avoid divide by zero and ensure at least 1 pixel big
1567 gdouble dx = (oldx - midx) / len;
1568 gdouble dy = (oldy - midy) / len;
1569 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1570 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1580 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1582 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1583 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1585 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1587 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1588 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 ));
1592 * If points are the same in display coordinates, don't draw.
1594 if ( x != oldx || y != oldy )
1596 if ( draw_track_outline )
1597 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1599 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1605 * If points are the same in display coordinates, don't draw.
1607 if ( x != oldx && y != oldy )
1609 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1610 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1620 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1622 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1623 trw_layer_draw_track ( id, track, dp, FALSE );
1627 static void cached_pixbuf_free ( CachedPixbuf *cp )
1629 g_object_unref ( G_OBJECT(cp->pixbuf) );
1630 g_free ( cp->image );
1633 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1635 return strcmp ( cp->image, name );
1638 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1641 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1642 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1643 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1646 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1648 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1650 if ( wp->image && dp->vtl->drawimages )
1652 GdkPixbuf *pixbuf = NULL;
1655 if ( dp->vtl->image_alpha == 0)
1658 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1660 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1663 gchar *image = wp->image;
1664 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1665 if ( ! regularthumb )
1667 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1668 image = "\x12\x00"; /* this shouldn't occur naturally. */
1672 CachedPixbuf *cp = NULL;
1673 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1674 if ( dp->vtl->image_size == 128 )
1675 cp->pixbuf = regularthumb;
1678 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1679 g_assert ( cp->pixbuf );
1680 g_object_unref ( G_OBJECT(regularthumb) );
1682 cp->image = g_strdup ( image );
1684 /* needed so 'click picture' tool knows how big the pic is; we don't
1685 * store it in cp because they may have been freed already. */
1686 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1687 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1689 g_queue_push_head ( dp->vtl->image_cache, cp );
1690 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1691 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1693 pixbuf = cp->pixbuf;
1697 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1703 w = gdk_pixbuf_get_width ( pixbuf );
1704 h = gdk_pixbuf_get_height ( pixbuf );
1706 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1708 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1709 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1710 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1711 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
1712 // Highlighted - so draw a little border around the chosen one
1713 // single line seems a little weak so draw 2 of them
1714 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1715 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1716 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1717 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1720 if ( dp->vtl->image_alpha == 255 )
1721 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1723 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1725 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1729 // Draw appropriate symbol - either symbol image or simple types
1730 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
1731 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 );
1733 else if ( wp == dp->vtl->current_wp ) {
1734 switch ( dp->vtl->wp_symbol ) {
1735 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;
1736 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;
1737 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;
1738 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 );
1739 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 );
1743 switch ( dp->vtl->wp_symbol ) {
1744 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;
1745 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;
1746 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;
1747 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 );
1748 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;
1752 if ( dp->vtl->drawlabels )
1754 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1755 gint label_x, label_y;
1757 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1759 // Could this stored in the waypoint rather than recreating each pass?
1760 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
1762 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1763 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1765 // Fallback if parse failure
1766 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1768 g_free ( wp_label_markup );
1770 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1771 label_x = x - width/2;
1772 if ( wp->symbol_pixbuf )
1773 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
1775 label_y = y - dp->vtl->wp_size - height - 2;
1777 /* if highlight mode on, then draw background text in highlight colour */
1778 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1779 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1780 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1781 wp == vik_window_get_selected_waypoint ( dp->vw ) )
1782 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1784 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1787 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1789 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1794 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1796 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
1797 trw_layer_draw_waypoint ( id, wp, dp );
1801 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1803 static struct DrawingParams dp;
1804 g_assert ( l != NULL );
1806 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1808 if ( l->tracks_visible )
1809 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1811 if ( l->routes_visible )
1812 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1814 if (l->waypoints_visible)
1815 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
1818 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1821 if ( vtl->track_bg_gc )
1823 g_object_unref ( vtl->track_bg_gc );
1824 vtl->track_bg_gc = NULL;
1826 if ( vtl->track_1color_gc )
1828 g_object_unref ( vtl->track_1color_gc );
1829 vtl->track_1color_gc = NULL;
1831 if ( vtl->current_track_gc )
1833 g_object_unref ( vtl->current_track_gc );
1834 vtl->current_track_gc = NULL;
1836 if ( vtl->current_track_newpoint_gc )
1838 g_object_unref ( vtl->current_track_newpoint_gc );
1839 vtl->current_track_newpoint_gc = NULL;
1842 if ( ! vtl->track_gc )
1844 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1845 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1846 g_array_free ( vtl->track_gc, TRUE );
1847 vtl->track_gc = NULL;
1850 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1852 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1853 gint width = vtl->line_thickness;
1855 if ( vtl->track_gc )
1856 trw_layer_free_track_gcs ( vtl );
1858 if ( vtl->track_bg_gc )
1859 g_object_unref ( vtl->track_bg_gc );
1860 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
1862 // Ensure new track drawing heeds line thickness setting
1863 // however always have a minium of 2, as 1 pixel is really narrow
1864 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1866 if ( vtl->current_track_gc )
1867 g_object_unref ( vtl->current_track_gc );
1868 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1869 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1871 // 'newpoint' gc is exactly the same as the current track gc
1872 if ( vtl->current_track_newpoint_gc )
1873 g_object_unref ( vtl->current_track_newpoint_gc );
1874 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1875 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1877 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1879 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1880 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1882 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1883 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1884 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1886 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
1888 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1891 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1893 VikTrwLayer *rv = trw_layer_new1 ( vp );
1894 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1896 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
1897 /* early exit, as the rest is GUI related */
1901 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1902 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
1904 trw_layer_new_track_gcs ( rv, vp );
1906 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
1907 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
1908 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
1909 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
1911 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1913 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1918 #define SMALL_ICON_SIZE 18
1920 * Can accept a null symbol, and may return null value
1922 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1924 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1925 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1926 // So needing a small icon for the treeview may need some resizing:
1927 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1928 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1932 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1934 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1936 GdkPixbuf *pixbuf = NULL;
1938 if ( track->has_color ) {
1939 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1940 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1941 // Here is some magic found to do the conversion
1942 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1943 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1944 ((track->color.green & 0xff00) << 8) |
1945 (track->color.blue & 0xff00);
1947 gdk_pixbuf_fill ( pixbuf, pixel );
1950 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1951 vik_treeview_add_sublayer_alphabetized ( (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 );
1953 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 );
1957 g_object_unref (pixbuf);
1959 *new_iter = *((GtkTreeIter *) pass_along[1]);
1960 if ( track->is_route )
1961 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1963 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1965 if ( ! track->visible )
1966 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1969 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1971 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1973 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1974 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
1976 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 );
1979 *new_iter = *((GtkTreeIter *) pass_along[1]);
1980 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1982 if ( ! wp->visible )
1983 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1986 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1988 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1989 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1991 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1995 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1997 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1998 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2000 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2004 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2006 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2007 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2009 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2013 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2016 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2018 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2019 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2020 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2022 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
2025 if ( g_hash_table_size (vtl->routes) > 0 ) {
2027 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2029 pass_along[0] = &(vtl->routes_iter);
2030 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2032 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2034 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2037 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2038 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2040 pass_along[0] = &(vtl->waypoints_iter);
2041 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2043 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2045 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2050 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2054 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2055 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2056 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2057 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2059 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2061 return (t->visible ^= 1);
2065 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2067 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2069 return (t->visible ^= 1);
2073 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2075 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2077 return (t->visible ^= 1);
2086 * Return a property about tracks for this layer
2088 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2090 return vtl->line_thickness;
2093 // Structure to hold multiple track information for a layer
2102 * Build up layer multiple track information via updating the tooltip_tracks structure
2104 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2106 tt->length = tt->length + vik_track_get_length (tr);
2108 // Ensure times are available
2109 if ( tr->trackpoints &&
2110 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2111 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2114 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2115 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2117 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2118 // Hence initialize to the first 'proper' value
2119 if ( tt->start_time == 0 )
2120 tt->start_time = t1;
2121 if ( tt->end_time == 0 )
2124 // Update find the earliest / last times
2125 if ( t1 < tt->start_time )
2126 tt->start_time = t1;
2127 if ( t2 > tt->end_time )
2130 // Keep track of total time
2131 // there maybe gaps within a track (eg segments)
2132 // but this should be generally good enough for a simple indicator
2133 tt->duration = tt->duration + (int)(t2-t1);
2138 * Generate tooltip text for the layer.
2139 * This is relatively complicated as it considers information for
2140 * no tracks, a single track or multiple tracks
2141 * (which may or may not have timing information)
2143 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2154 static gchar tmp_buf[128];
2157 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2159 // Safety check - I think these should always be valid
2160 if ( vtl->tracks && vtl->waypoints ) {
2161 tooltip_tracks tt = { 0.0, 0, 0 };
2162 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2164 GDate* gdate_start = g_date_new ();
2165 g_date_set_time_t (gdate_start, tt.start_time);
2167 GDate* gdate_end = g_date_new ();
2168 g_date_set_time_t (gdate_end, tt.end_time);
2170 if ( g_date_compare (gdate_start, gdate_end) ) {
2171 // Dates differ so print range on separate line
2172 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2173 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2174 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2177 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2178 if ( tt.start_time != 0 )
2179 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2183 if ( tt.length > 0.0 ) {
2184 gdouble len_in_units;
2186 // Setup info dependent on distance units
2187 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2188 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2189 len_in_units = VIK_METERS_TO_MILES(tt.length);
2192 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2193 len_in_units = tt.length/1000.0;
2196 // Timing information if available
2198 if ( tt.duration > 0 ) {
2199 g_snprintf (tbuf1, sizeof(tbuf1),
2200 _(" in %d:%02d hrs:mins"),
2201 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2203 g_snprintf (tbuf2, sizeof(tbuf2),
2204 _("\n%sTotal Length %.1f %s%s"),
2205 tbuf3, len_in_units, tbuf4, tbuf1);
2208 // Put together all the elements to form compact tooltip text
2209 g_snprintf (tmp_buf, sizeof(tmp_buf),
2210 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2211 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2213 g_date_free (gdate_start);
2214 g_date_free (gdate_end);
2221 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2225 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2227 // Very simple tooltip - may expand detail in the future...
2228 static gchar tmp_buf[32];
2229 g_snprintf (tmp_buf, sizeof(tmp_buf),
2231 g_hash_table_size (l->tracks));
2235 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2237 // Very simple tooltip - may expand detail in the future...
2238 static gchar tmp_buf[32];
2239 g_snprintf (tmp_buf, sizeof(tmp_buf),
2241 g_hash_table_size (l->routes));
2246 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2247 // Same tooltip for a route
2248 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2251 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2252 tr = g_hash_table_lookup ( l->tracks, sublayer );
2254 tr = g_hash_table_lookup ( l->routes, sublayer );
2257 // Could be a better way of handling strings - but this works...
2258 gchar time_buf1[20];
2259 gchar time_buf2[20];
2260 time_buf1[0] = '\0';
2261 time_buf2[0] = '\0';
2262 static gchar tmp_buf[100];
2263 // Compact info: Short date eg (11/20/99), duration and length
2264 // Hopefully these are the things that are most useful and so promoted into the tooltip
2265 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2266 // %x The preferred date representation for the current locale without the time.
2267 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2268 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2269 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2271 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2274 // Get length and consider the appropriate distance units
2275 gdouble tr_len = vik_track_get_length(tr);
2276 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2277 switch (dist_units) {
2278 case VIK_UNITS_DISTANCE_KILOMETRES:
2279 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2281 case VIK_UNITS_DISTANCE_MILES:
2282 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2291 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2293 // Very simple tooltip - may expand detail in the future...
2294 static gchar tmp_buf[32];
2295 g_snprintf (tmp_buf, sizeof(tmp_buf),
2297 g_hash_table_size (l->waypoints));
2301 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2303 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2304 // NB It's OK to return NULL
2309 return w->description;
2319 * Function to show basic track point information on the statusbar
2321 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2324 switch (a_vik_get_units_height ()) {
2325 case VIK_UNITS_HEIGHT_FEET:
2326 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2329 //VIK_UNITS_HEIGHT_METRES:
2330 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2335 if ( trkpt->has_timestamp ) {
2336 // Compact date time format
2337 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2341 // Position is put later on, as this bit may not be seen if the display is not big enough,
2342 // one can easily use the current pointer position to see this if needed
2343 gchar *lat = NULL, *lon = NULL;
2344 static struct LatLon ll;
2345 vik_coord_to_latlon (&(trkpt->coord), &ll);
2346 a_coords_latlon_to_string ( &ll, &lat, &lon );
2349 // Again is put later on, as this bit may not be seen if the display is not big enough
2350 // trackname can be seen from the treeview (when enabled)
2351 // Also name could be very long to not leave room for anything else
2354 if ( vtl->current_tp_track ) {
2355 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2358 // Combine parts to make overall message
2359 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2360 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2367 * Function to show basic waypoint information on the statusbar
2369 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2372 switch (a_vik_get_units_height ()) {
2373 case VIK_UNITS_HEIGHT_FEET:
2374 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2377 //VIK_UNITS_HEIGHT_METRES:
2378 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2382 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2383 // one can easily use the current pointer position to see this if needed
2384 gchar *lat = NULL, *lon = NULL;
2385 static struct LatLon ll;
2386 vik_coord_to_latlon (&(wpt->coord), &ll);
2387 a_coords_latlon_to_string ( &ll, &lat, &lon );
2389 // Combine parts to make overall message
2392 // Add comment if available
2393 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2395 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2396 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2403 * General layer selection function, find out which bit is selected and take appropriate action
2405 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2408 l->current_wp = NULL;
2409 l->current_wp_id = NULL;
2410 trw_layer_cancel_current_tp ( l, FALSE );
2413 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2417 case VIK_TREEVIEW_TYPE_LAYER:
2419 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2420 /* Mark for redraw */
2425 case VIK_TREEVIEW_TYPE_SUBLAYER:
2429 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2431 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2432 /* Mark for redraw */
2436 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2438 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2439 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2440 /* Mark for redraw */
2444 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2446 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2447 /* Mark for redraw */
2451 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2453 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2454 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2455 /* Mark for redraw */
2459 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2461 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2462 /* Mark for redraw */
2466 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2468 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2470 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2471 // Show some waypoint info
2472 set_statusbar_msg_info_wpt ( l, wpt );
2473 /* Mark for redraw */
2480 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2489 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2494 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2499 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2504 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2506 return l->waypoints;
2509 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2511 return ! ( g_hash_table_size ( vtl->tracks ) ||
2512 g_hash_table_size ( vtl->routes ) ||
2513 g_hash_table_size ( vtl->waypoints ) );
2517 * ATM use a case sensitive find
2518 * Finds the first one
2520 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2522 if ( wp && wp->name )
2523 if ( ! strcmp ( wp->name, name ) )
2529 * Get waypoint by name - not guaranteed to be unique
2530 * Finds the first one
2532 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2534 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2538 * ATM use a case sensitive find
2539 * Finds the first one
2541 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2543 if ( trk && trk->name )
2544 if ( ! strcmp ( trk->name, name ) )
2550 * Get track by name - not guaranteed to be unique
2551 * Finds the first one
2553 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2555 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2559 * Get route by name - not guaranteed to be unique
2560 * Finds the first one
2562 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2564 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2567 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] )
2569 static VikCoord fixme;
2570 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2571 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2572 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2573 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2574 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2575 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2576 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2577 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2578 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2581 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2583 GList *tr = trk->trackpoints;
2584 static VikCoord fixme;
2588 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2589 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2590 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2591 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2592 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2593 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2594 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2595 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2596 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2601 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2603 // Continually reuse maxmin to find the latest maximum and minimum values
2604 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2605 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2606 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2609 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2611 /* 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... */
2612 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2613 trw_layer_find_maxmin (vtl, maxmin);
2614 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2618 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2619 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2624 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2627 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2628 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2630 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2633 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2635 /* First set the center [in case previously viewing from elsewhere] */
2636 /* Then loop through zoom levels until provided positions are in view */
2637 /* This method is not particularly fast - but should work well enough */
2638 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2640 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2641 vik_viewport_set_center_coord ( vvp, &coord );
2643 /* Convert into definite 'smallest' and 'largest' positions */
2644 struct LatLon minmin;
2645 if ( maxmin[0].lat < maxmin[1].lat )
2646 minmin.lat = maxmin[0].lat;
2648 minmin.lat = maxmin[1].lat;
2650 struct LatLon maxmax;
2651 if ( maxmin[0].lon > maxmin[1].lon )
2652 maxmax.lon = maxmin[0].lon;
2654 maxmax.lon = maxmin[1].lon;
2656 /* Never zoom in too far - generally not that useful, as too close ! */
2657 /* Always recalculate the 'best' zoom level */
2659 vik_viewport_set_zoom ( vvp, zoom );
2661 gdouble min_lat, max_lat, min_lon, max_lon;
2662 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2663 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2664 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2665 /* NB I think the logic used in this test to determine if the bounds is within view
2666 fails if track goes across 180 degrees longitude.
2667 Hopefully that situation is not too common...
2668 Mind you viking doesn't really do edge locations to well anyway */
2669 if ( min_lat < minmin.lat &&
2670 max_lat > minmin.lat &&
2671 min_lon < maxmax.lon &&
2672 max_lon > maxmax.lon )
2673 /* Found within zoom level */
2678 vik_viewport_set_zoom ( vvp, zoom );
2682 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2684 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2685 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2686 trw_layer_find_maxmin (vtl, maxmin);
2687 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2690 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2695 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2697 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])) ) ) {
2698 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2701 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2704 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2706 GtkWidget *file_selector;
2708 gboolean failed = FALSE;
2709 file_selector = gtk_file_chooser_dialog_new (title,
2711 GTK_FILE_CHOOSER_ACTION_SAVE,
2712 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2713 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2715 gchar *cwd = g_get_current_dir();
2717 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
2721 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2723 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2725 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2726 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2728 gtk_widget_hide ( file_selector );
2729 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2730 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2731 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2736 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2738 gtk_widget_hide ( file_selector );
2739 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2740 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2741 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2746 gtk_widget_destroy ( file_selector );
2748 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2751 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2753 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2756 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2758 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2761 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2763 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2764 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2765 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2766 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2768 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2770 g_free ( auto_save_name );
2773 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2775 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2776 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2777 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2778 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2780 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2782 g_free ( auto_save_name );
2786 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2789 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2791 gchar *name_used = NULL;
2794 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2795 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2796 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2797 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2799 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2803 gchar *quoted_file = g_shell_quote ( name_used );
2804 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2805 g_free ( quoted_file );
2806 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2808 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2809 g_error_free ( err );
2813 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2814 //g_remove ( name_used );
2815 // Perhaps should be deleted when the program ends?
2816 // For now leave it to the user to delete it / use system temp cleanup methods.
2817 g_free ( name_used );
2821 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2823 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2826 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2828 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2831 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2833 gpointer layer_and_vlp[2];
2834 layer_and_vlp[0] = pass_along[0];
2835 layer_and_vlp[1] = pass_along[1];
2837 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2839 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2840 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2842 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2844 if ( !trk || !trk->name )
2847 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2848 gchar *auto_save_name = g_strdup ( trk->name );
2849 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2850 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2852 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2854 g_free ( auto_save_name );
2858 VikWaypoint *wp; // input
2859 gpointer uuid; // output
2862 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2864 wpu_udata *user_data = udata;
2865 if ( wp == user_data->wp ) {
2866 user_data->uuid = id;
2872 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2874 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2875 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2876 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2878 GTK_RESPONSE_REJECT,
2880 GTK_RESPONSE_ACCEPT,
2883 GtkWidget *label, *entry;
2884 label = gtk_label_new(_("Waypoint Name:"));
2885 entry = gtk_entry_new();
2887 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
2888 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
2889 gtk_widget_show_all ( label );
2890 gtk_widget_show_all ( entry );
2892 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2894 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2896 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2897 // Find *first* wp with the given name
2898 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2901 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2904 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2905 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2907 // Find and select on the side panel
2912 // Hmmm, want key of it
2913 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2915 if ( wpf && udata.uuid ) {
2916 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2917 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2926 gtk_widget_destroy ( dia );
2929 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2931 gchar *default_name = highest_wp_number_get(vtl);
2932 VikWaypoint *wp = vik_waypoint_new();
2933 gchar *returned_name;
2935 wp->coord = *def_coord;
2937 // Attempt to auto set height if DEM data is available
2938 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2939 if ( elev != VIK_DEM_INVALID_ELEVATION )
2940 wp->altitude = (gdouble)elev;
2942 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
2944 if ( returned_name )
2947 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2948 g_free (default_name);
2949 g_free (returned_name);
2952 g_free (default_name);
2953 vik_waypoint_free(wp);
2957 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2959 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2960 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2961 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2962 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2963 VikViewport *vvp = vik_window_viewport(vw);
2965 // Note the order is max part first then min part - thus reverse order of use in min_max function:
2966 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
2967 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2968 trw_layer_calculate_bounds_waypoints ( vtl );
2969 vik_layers_panel_emit_update ( vlp );
2972 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2974 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2975 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2976 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2978 trw_layer_find_maxmin (vtl, maxmin);
2979 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2980 trw_layer_calculate_bounds_waypoints ( vtl );
2981 vik_layers_panel_emit_update ( vlp );
2984 #ifdef VIK_CONFIG_GEOTAG
2985 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2987 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2989 // Update directly - not changing the mtime
2990 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2993 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2995 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2998 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3002 * Use code in separate file for this feature as reasonably complex
3004 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3006 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3007 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3008 // Unset so can be reverified later if necessary
3009 vtl->has_verified_thumbnails = FALSE;
3011 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3017 static void trw_layer_geotagging ( gpointer lav[2] )
3019 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3020 // Unset so can be reverified later if necessary
3021 vtl->has_verified_thumbnails = FALSE;
3023 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3030 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3033 * Acquire into this TRW Layer straight from GPS Device
3035 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3037 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3038 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3039 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3040 VikViewport *vvp = vik_window_viewport(vw);
3042 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3043 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3046 #ifdef VIK_CONFIG_GOOGLE
3048 * Acquire into this TRW Layer from Google Directions
3050 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
3052 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3053 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3054 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3055 VikViewport *vvp = vik_window_viewport(vw);
3057 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface, NULL, NULL );
3061 #ifdef VIK_CONFIG_OPENSTREETMAP
3063 * Acquire into this TRW Layer from OSM
3065 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3067 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3068 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3069 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3070 VikViewport *vvp = vik_window_viewport(vw);
3072 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3076 * Acquire into this TRW Layer from OSM for 'My' Traces
3078 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3080 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3081 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3082 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3083 VikViewport *vvp = vik_window_viewport(vw);
3085 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3089 #ifdef VIK_CONFIG_GEOCACHES
3091 * Acquire into this TRW Layer from Geocaching.com
3093 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3095 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3096 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3097 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3098 VikViewport *vvp = vik_window_viewport(vw);
3100 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3104 #ifdef VIK_CONFIG_GEOTAG
3106 * Acquire into this TRW Layer from images
3108 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3110 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3111 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3112 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3113 VikViewport *vvp = vik_window_viewport(vw);
3115 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3116 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3118 // Reverify thumbnails as they may have changed
3119 vtl->has_verified_thumbnails = FALSE;
3120 trw_layer_verify_thumbnails ( vtl, NULL );
3124 static void trw_layer_gps_upload ( gpointer lav[2] )
3126 gpointer pass_along[6];
3127 pass_along[0] = lav[0];
3128 pass_along[1] = lav[1];
3129 pass_along[2] = NULL; // No track - operate on the layer
3130 pass_along[3] = NULL;
3131 pass_along[4] = NULL;
3132 pass_along[5] = NULL;
3134 trw_layer_gps_upload_any ( pass_along );
3138 * If pass_along[3] is defined that this will upload just that track
3140 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3142 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3143 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3145 // May not actually get a track here as pass_along[2&3] can be null
3146 VikTrack *track = NULL;
3147 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3148 gboolean xfer_all = FALSE;
3150 if ( pass_along[2] ) {
3152 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3153 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3156 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3157 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3160 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3163 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3167 else if ( !pass_along[4] )
3168 xfer_all = TRUE; // i.e. whole layer
3170 if (track && !track->visible) {
3171 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3175 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3176 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3177 GTK_DIALOG_DESTROY_WITH_PARENT,
3179 GTK_RESPONSE_ACCEPT,
3181 GTK_RESPONSE_REJECT,
3184 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3185 GtkWidget *response_w = NULL;
3186 #if GTK_CHECK_VERSION (2, 20, 0)
3187 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3191 gtk_widget_grab_focus ( response_w );
3193 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3195 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3196 datasource_gps_clean_up ( dgs );
3197 gtk_widget_destroy ( dialog );
3201 // Get info from reused datasource dialog widgets
3202 gchar* protocol = datasource_gps_get_protocol ( dgs );
3203 gchar* port = datasource_gps_get_descriptor ( dgs );
3204 // NB don't free the above strings as they're references to values held elsewhere
3205 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3206 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3207 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3208 gboolean turn_off = datasource_gps_get_off ( dgs );
3210 gtk_widget_destroy ( dialog );
3212 // When called from the viewport - work the corresponding layerspanel:
3214 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3217 // Apply settings to transfer to the GPS device
3224 vik_layers_panel_get_viewport (vlp),
3233 * Acquire into this TRW Layer from any GPS Babel supported file
3235 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3237 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3238 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3239 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3240 VikViewport *vvp = vik_window_viewport(vw);
3242 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3245 static void trw_layer_new_wp ( gpointer lav[2] )
3247 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3248 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3249 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3250 instead return true if you want to update. */
3251 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 ) {
3252 trw_layer_calculate_bounds_waypoints ( vtl );
3253 vik_layers_panel_emit_update ( vlp );
3257 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3259 vtl->current_track = vik_track_new();
3260 vtl->current_track->visible = TRUE;
3261 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3262 // Create track with the preferred colour from the layer properties
3263 vtl->current_track->color = vtl->track_color;
3265 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3266 vtl->current_track->has_color = TRUE;
3267 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3270 static void trw_layer_new_track ( gpointer lav[2] )
3272 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3274 if ( ! vtl->current_track ) {
3275 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3276 new_track_create_common ( vtl, name );
3278 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3282 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3284 vtl->current_track = vik_track_new();
3285 vtl->current_track->visible = TRUE;
3286 vtl->current_track->is_route = TRUE;
3287 // By default make all routes red
3288 vtl->current_track->has_color = TRUE;
3289 gdk_color_parse ( "red", &vtl->current_track->color );
3290 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3293 static void trw_layer_new_route ( gpointer lav[2] )
3295 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3297 if ( ! vtl->current_track ) {
3298 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3299 new_route_create_common ( vtl, name );
3300 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3304 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3306 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3307 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3309 if ( g_hash_table_size (vtl->routes) > 0 ) {
3310 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3311 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3312 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3313 vik_layers_panel_emit_update ( vlp );
3318 static void trw_layer_finish_track ( gpointer lav[2] )
3320 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3321 vtl->current_track = NULL;
3322 vik_layer_emit_update ( VIK_LAYER(vtl) );
3325 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3327 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3328 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3330 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3331 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3332 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3333 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3334 vik_layers_panel_emit_update ( vlp );
3338 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3340 /* NB do not care if wp is visible or not */
3341 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3344 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3346 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3347 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3349 /* Only 1 waypoint - jump straight to it */
3350 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3351 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3352 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3354 /* If at least 2 waypoints - find center and then zoom to fit */
3355 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3357 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3358 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3359 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3362 vik_layers_panel_emit_update ( vlp );
3365 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3367 static gpointer pass_along[2];
3369 GtkWidget *export_submenu;
3370 pass_along[0] = vtl;
3371 pass_along[1] = vlp;
3373 item = gtk_menu_item_new();
3374 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3375 gtk_widget_show ( item );
3377 if ( vtl->current_track ) {
3378 if ( vtl->current_track->is_route )
3379 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3381 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3382 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3383 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3384 gtk_widget_show ( item );
3387 item = gtk_menu_item_new ();
3388 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3389 gtk_widget_show ( item );
3392 /* Now with icons */
3393 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3394 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3395 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3396 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3397 gtk_widget_show ( item );
3399 GtkWidget *view_submenu = gtk_menu_new();
3400 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3401 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3402 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3403 gtk_widget_show ( item );
3404 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3406 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3407 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3408 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3409 gtk_widget_show ( item );
3411 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3412 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3413 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3414 gtk_widget_show ( item );
3416 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3417 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3418 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3419 gtk_widget_show ( item );
3421 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3422 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3423 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3424 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3425 gtk_widget_show ( item );
3427 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3428 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3429 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3430 gtk_widget_show ( item );
3432 export_submenu = gtk_menu_new ();
3433 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3434 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3435 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3436 gtk_widget_show ( item );
3437 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3439 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3440 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3441 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3442 gtk_widget_show ( item );
3444 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3445 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3446 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3447 gtk_widget_show ( item );
3449 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3450 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3451 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3452 gtk_widget_show ( item );
3454 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3455 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3456 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3457 gtk_widget_show ( item );
3459 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3460 item = gtk_menu_item_new_with_mnemonic ( external1 );
3461 g_free ( external1 );
3462 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3463 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3464 gtk_widget_show ( item );
3466 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3467 item = gtk_menu_item_new_with_mnemonic ( external2 );
3468 g_free ( external2 );
3469 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3470 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3471 gtk_widget_show ( item );
3473 GtkWidget *new_submenu = gtk_menu_new();
3474 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3475 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3476 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3477 gtk_widget_show(item);
3478 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3480 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3481 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3482 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3483 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3484 gtk_widget_show ( item );
3486 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3489 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3490 gtk_widget_show ( item );
3491 // Make it available only when a new track *not* already in progress
3492 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3494 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3495 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3496 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3497 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3498 gtk_widget_show ( item );
3499 // Make it available only when a new track *not* already in progress
3500 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3502 #ifdef VIK_CONFIG_GEOTAG
3503 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3505 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3506 gtk_widget_show ( item );
3509 GtkWidget *acquire_submenu = gtk_menu_new ();
3510 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3512 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3513 gtk_widget_show ( item );
3514 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3516 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3517 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3518 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3519 gtk_widget_show ( item );
3521 #ifdef VIK_CONFIG_GOOGLE
3522 item = gtk_menu_item_new_with_mnemonic ( _("From Google _Directions...") );
3523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3524 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3525 gtk_widget_show ( item );
3528 #ifdef VIK_CONFIG_OPENSTREETMAP
3529 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3530 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3531 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3532 gtk_widget_show ( item );
3534 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3535 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3536 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3537 gtk_widget_show ( item );
3540 #ifdef VIK_CONFIG_GEONAMES
3541 GtkWidget *wikipedia_submenu = gtk_menu_new();
3542 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3543 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3544 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3545 gtk_widget_show(item);
3546 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3548 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3549 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3550 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3551 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3552 gtk_widget_show ( item );
3554 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3555 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3556 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3557 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3558 gtk_widget_show ( item );
3561 #ifdef VIK_CONFIG_GEOCACHES
3562 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3563 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3564 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3565 gtk_widget_show ( item );
3568 #ifdef VIK_CONFIG_GEOTAG
3569 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3570 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3571 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3572 gtk_widget_show ( item );
3575 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3576 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3577 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3578 gtk_widget_show ( item );
3580 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3582 GtkWidget *upload_submenu = gtk_menu_new ();
3583 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3584 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3585 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3586 gtk_widget_show ( item );
3587 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3589 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3590 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3591 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3592 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3593 gtk_widget_show ( item );
3595 #ifdef VIK_CONFIG_OPENSTREETMAP
3596 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3597 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3598 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3599 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3600 gtk_widget_show ( item );
3603 GtkWidget *delete_submenu = gtk_menu_new ();
3604 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3605 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3606 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3607 gtk_widget_show ( item );
3608 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3610 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3611 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3613 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3614 gtk_widget_show ( item );
3616 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3617 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3618 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3619 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3620 gtk_widget_show ( item );
3622 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3623 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3624 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3625 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3626 gtk_widget_show ( item );
3628 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3629 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3630 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3631 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3632 gtk_widget_show ( item );
3634 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3635 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3636 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3637 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3638 gtk_widget_show ( item );
3640 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3641 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3642 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3643 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3644 gtk_widget_show ( item );
3646 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3647 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3649 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3650 gtk_widget_show ( item );
3653 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3654 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3656 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3657 gtk_widget_show ( item );
3661 // Fake Waypoint UUIDs vi simple increasing integer
3662 static guint wp_uuid = 0;
3664 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3668 vik_waypoint_set_name (wp, name);
3670 if ( VIK_LAYER(vtl)->realized )
3672 // Do we need to create the sublayer:
3673 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3674 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3677 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3679 // Visibility column always needed for waypoints
3680 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3681 vik_treeview_add_sublayer_alphabetized ( 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 );
3683 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 );
3685 // Actual setting of visibility dependent on the waypoint
3686 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3688 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3691 highest_wp_number_add_wp(vtl, name);
3692 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3696 // Fake Track UUIDs vi simple increasing integer
3697 static guint tr_uuid = 0;
3699 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3703 vik_track_set_name (t, name);
3705 if ( VIK_LAYER(vtl)->realized )
3707 // Do we need to create the sublayer:
3708 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3709 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3712 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3713 // Visibility column always needed for tracks
3714 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3715 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
3717 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 );
3719 // Actual setting of visibility dependent on the track
3720 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3722 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3725 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3727 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3730 // Fake Route UUIDs vi simple increasing integer
3731 static guint rt_uuid = 0;
3733 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3737 vik_track_set_name (t, name);
3739 if ( VIK_LAYER(vtl)->realized )
3741 // Do we need to create the sublayer:
3742 if ( g_hash_table_size (vtl->routes) == 0 ) {
3743 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3746 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3747 // Visibility column always needed for tracks
3748 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3749 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3751 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 );
3753 // Actual setting of visibility dependent on the track
3754 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3756 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3759 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3761 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3764 /* to be called whenever a track has been deleted or may have been changed. */
3765 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3767 if (vtl->current_tp_track == trk )
3768 trw_layer_cancel_current_tp ( vtl, FALSE );
3772 * Normally this is done to due the waypoint size preference having changed
3774 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
3776 GHashTableIter iter;
3777 gpointer key, value;
3780 g_hash_table_iter_init ( &iter, vtl->waypoints );
3781 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
3782 VikWaypoint *wp = VIK_WAYPOINT(value);
3784 // Reapply symbol setting to update the pixbuf
3785 gchar *tmp_symbol = g_strdup ( wp->symbol );
3786 vik_waypoint_set_symbol ( wp, tmp_symbol );
3787 g_free ( tmp_symbol );
3792 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3795 gchar *newname = g_strdup(name);
3800 switch ( sublayer_type ) {
3801 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3802 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3804 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3805 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3808 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3811 // If found a name already in use try adding 1 to it and we try again
3813 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3815 newname = new_newname;
3818 } while ( id != NULL);
3823 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3825 // No more uniqueness of name forced when loading from a file
3826 // This now makes this function a little redunant as we just flow the parameters through
3827 vik_trw_layer_add_waypoint ( vtl, name, wp );
3830 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3832 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3833 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3834 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3835 vik_track_free ( tr );
3836 vtl->route_finder_append = FALSE; /* this means we have added it */
3839 // No more uniqueness of name forced when loading from a file
3841 vik_trw_layer_add_route ( vtl, name, tr );
3843 vik_trw_layer_add_track ( vtl, name, tr );
3845 if ( vtl->route_finder_check_added_track ) {
3846 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3847 vtl->route_finder_added_track = tr;
3852 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3854 *l = g_list_append(*l, id);
3858 * Move an item from one TRW layer to another TRW layer
3860 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3862 gboolean rename = ( vtl_src != vtl_dest );
3864 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3865 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3869 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3871 newname = g_strdup ( trk->name );
3873 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3874 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3875 vik_trw_layer_delete_track ( vtl_src, trk );
3878 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3879 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3883 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3885 newname = g_strdup ( trk->name );
3887 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3888 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3889 vik_trw_layer_delete_route ( vtl_src, trk );
3892 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3893 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3897 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
3899 newname = g_strdup ( wp->name );
3901 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3902 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3903 trw_layer_delete_waypoint ( vtl_src, wp );
3905 // Recalculate bounds even if not renamed as maybe dragged between layers
3906 trw_layer_calculate_bounds_waypoints ( vtl_dest );
3907 trw_layer_calculate_bounds_waypoints ( vtl_src );
3911 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3913 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3914 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3916 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3917 GList *items = NULL;
3920 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3921 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3923 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3924 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3926 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3927 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3932 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3933 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3935 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3936 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3938 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3945 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3946 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3951 VikTrack *trk; // input
3952 gpointer uuid; // output
3955 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3957 trku_udata *user_data = udata;
3958 if ( trk == user_data->trk ) {
3959 user_data->uuid = id;
3965 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3967 gboolean was_visible = FALSE;
3969 if ( trk && trk->name ) {
3971 if ( trk == vtl->current_track ) {
3972 vtl->current_track = NULL;
3973 vtl->current_tp_track = NULL;
3974 vtl->current_tp_id = NULL;
3975 vtl->moving_tp = FALSE;
3978 was_visible = trk->visible;
3980 if ( trk == vtl->route_finder_current_track )
3981 vtl->route_finder_current_track = NULL;
3983 if ( trk == vtl->route_finder_added_track )
3984 vtl->route_finder_added_track = NULL;
3990 // Hmmm, want key of it
3991 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3993 if ( trkf && udata.uuid ) {
3994 /* could be current_tp, so we have to check */
3995 trw_layer_cancel_tps_of_track ( vtl, trk );
3997 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4000 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4001 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4002 g_hash_table_remove ( vtl->tracks, udata.uuid );
4004 // If last sublayer, then remove sublayer container
4005 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4006 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4014 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4016 gboolean was_visible = FALSE;
4018 if ( trk && trk->name ) {
4020 if ( trk == vtl->current_track ) {
4021 vtl->current_track = NULL;
4022 vtl->current_tp_track = NULL;
4023 vtl->current_tp_id = NULL;
4024 vtl->moving_tp = FALSE;
4027 was_visible = trk->visible;
4029 if ( trk == vtl->route_finder_current_track )
4030 vtl->route_finder_current_track = NULL;
4032 if ( trk == vtl->route_finder_added_track )
4033 vtl->route_finder_added_track = NULL;
4039 // Hmmm, want key of it
4040 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4042 if ( trkf && udata.uuid ) {
4043 /* could be current_tp, so we have to check */
4044 trw_layer_cancel_tps_of_track ( vtl, trk );
4046 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4049 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4050 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4051 g_hash_table_remove ( vtl->routes, udata.uuid );
4053 // If last sublayer, then remove sublayer container
4054 if ( g_hash_table_size (vtl->routes) == 0 ) {
4055 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4063 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4065 gboolean was_visible = FALSE;
4067 if ( wp && wp->name ) {
4069 if ( wp == vtl->current_wp ) {
4070 vtl->current_wp = NULL;
4071 vtl->current_wp_id = NULL;
4072 vtl->moving_wp = FALSE;
4075 was_visible = wp->visible;
4081 // Hmmm, want key of it
4082 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4084 if ( wpf && udata.uuid ) {
4085 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4088 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4089 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4091 highest_wp_number_remove_wp(vtl, wp->name);
4092 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4094 // If last sublayer, then remove sublayer container
4095 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4096 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4106 // Only for temporary use by trw_layer_delete_waypoint_by_name
4107 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4109 wpu_udata *user_data = udata;
4110 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4111 user_data->uuid = id;
4118 * Delete a waypoint by the given name
4119 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4120 * as there be multiple waypoints with the same name
4122 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4125 // Fake a waypoint with the given name
4126 udata.wp = vik_waypoint_new ();
4127 vik_waypoint_set_name (udata.wp, name);
4128 // Currently only the name is used in this waypoint find function
4131 // Hmmm, want key of it
4132 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4134 vik_waypoint_free (udata.wp);
4136 if ( wpf && udata.uuid )
4137 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4143 VikTrack *trk; // input
4144 gpointer uuid; // output
4147 // Only for temporary use by trw_layer_delete_track_by_name
4148 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4150 tpu_udata *user_data = udata;
4151 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4152 user_data->uuid = id;
4159 * Delete a track by the given name
4160 * NOTE: ATM this will delete the first encountered Track with the specified name
4161 * as there may be multiple tracks with the same name within the specified hash table
4163 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4166 // Fake a track with the given name
4167 udata.trk = vik_track_new ();
4168 vik_track_set_name (udata.trk, name);
4169 // Currently only the name is used in this waypoint find function
4172 // Hmmm, want key of it
4173 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4175 vik_track_free (udata.trk);
4177 if ( trkf && udata.uuid ) {
4178 // This could be a little better written...
4179 if ( vtl->tracks == ht_tracks )
4180 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4181 if ( vtl->routes == ht_tracks )
4182 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4189 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4191 vik_treeview_item_delete (vt, it );
4194 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4197 vtl->current_track = NULL;
4198 vtl->route_finder_current_track = NULL;
4199 vtl->route_finder_added_track = NULL;
4200 if (vtl->current_tp_track)
4201 trw_layer_cancel_current_tp(vtl, FALSE);
4203 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4204 g_hash_table_remove_all(vtl->routes_iters);
4205 g_hash_table_remove_all(vtl->routes);
4207 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4209 vik_layer_emit_update ( VIK_LAYER(vtl) );
4212 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4215 vtl->current_track = NULL;
4216 vtl->route_finder_current_track = NULL;
4217 vtl->route_finder_added_track = NULL;
4218 if (vtl->current_tp_track)
4219 trw_layer_cancel_current_tp(vtl, FALSE);
4221 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4222 g_hash_table_remove_all(vtl->tracks_iters);
4223 g_hash_table_remove_all(vtl->tracks);
4225 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4227 vik_layer_emit_update ( VIK_LAYER(vtl) );
4230 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4232 vtl->current_wp = NULL;
4233 vtl->current_wp_id = NULL;
4234 vtl->moving_wp = FALSE;
4236 highest_wp_number_reset(vtl);
4238 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4239 g_hash_table_remove_all(vtl->waypoints_iters);
4240 g_hash_table_remove_all(vtl->waypoints);
4242 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4244 vik_layer_emit_update ( VIK_LAYER(vtl) );
4247 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4249 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4250 // Get confirmation from the user
4251 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4252 _("Are you sure you want to delete all tracks in %s?"),
4253 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4254 vik_trw_layer_delete_all_tracks (vtl);
4257 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4259 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4260 // Get confirmation from the user
4261 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4262 _("Are you sure you want to delete all routes in %s?"),
4263 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4264 vik_trw_layer_delete_all_routes (vtl);
4267 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4269 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4270 // Get confirmation from the user
4271 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4272 _("Are you sure you want to delete all waypoints in %s?"),
4273 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4274 vik_trw_layer_delete_all_waypoints (vtl);
4277 static void trw_layer_delete_item ( gpointer pass_along[6] )
4279 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4280 gboolean was_visible = FALSE;
4281 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4283 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4284 if ( wp && wp->name ) {
4285 if ( GPOINTER_TO_INT ( pass_along[4]) )
4286 // Get confirmation from the user
4287 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4288 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4289 _("Are you sure you want to delete the waypoint \"%s\""),
4292 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4295 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4297 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4298 if ( trk && trk->name ) {
4299 if ( GPOINTER_TO_INT ( pass_along[4]) )
4300 // Get confirmation from the user
4301 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4302 _("Are you sure you want to delete the track \"%s\""),
4305 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4310 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4311 if ( trk && trk->name ) {
4312 if ( GPOINTER_TO_INT ( pass_along[4]) )
4313 // Get confirmation from the user
4314 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4315 _("Are you sure you want to delete the route \"%s\""),
4318 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4322 vik_layer_emit_update ( VIK_LAYER(vtl) );
4326 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4328 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4330 vik_waypoint_set_name ( wp, new_name );
4332 // Now update the treeview as well
4337 // Need key of it for treeview update
4338 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4340 if ( wpf && udataU.uuid ) {
4341 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4344 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4345 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4346 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, new_name );
4352 static void trw_layer_properties_item ( gpointer pass_along[7] )
4354 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4355 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4357 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4359 if ( wp && wp->name )
4361 gboolean updated = FALSE;
4362 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4364 trw_layer_waypoint_rename ( vtl, wp, new_name );
4366 if ( updated && pass_along[6] )
4367 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4369 if ( updated && VIK_LAYER(vtl)->visible )
4370 vik_layer_emit_update ( VIK_LAYER(vtl) );
4376 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4377 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4379 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4381 if ( tr && tr->name )
4383 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4386 pass_along[1], /* vlp */
4387 pass_along[5], /* vvp */
4388 pass_along[6]); /* iter */
4394 * Update the treeview of the track id - primarily to update the icon
4396 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4402 gpointer *trkf = NULL;
4403 if ( trk->is_route )
4404 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4406 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4408 if ( trkf && udata.uuid ) {
4410 GtkTreeIter *iter = NULL;
4411 if ( trk->is_route )
4412 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4414 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4417 // TODO: Make this a function
4418 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4419 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4420 ((trk->color.green & 0xff00) << 8) |
4421 (trk->color.blue & 0xff00);
4422 gdk_pixbuf_fill ( pixbuf, pixel );
4423 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4424 g_object_unref (pixbuf);
4431 Parameter 1 -> VikLayersPanel
4432 Parameter 2 -> VikLayer
4433 Parameter 3 -> VikViewport
4435 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4438 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4439 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4442 /* since vlp not set, vl & vvp should be valid instead! */
4444 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4445 vik_layer_emit_update ( VIK_LAYER(vl) );
4450 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4452 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4454 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4455 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4457 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4459 if ( track && track->trackpoints )
4460 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4463 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4465 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4467 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4468 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4470 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4472 if ( track && track->trackpoints )
4474 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4476 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4477 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4478 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4479 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4480 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4484 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4486 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4488 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4489 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4491 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4496 // Converting a track to a route can be a bit more complicated,
4497 // so give a chance to change our minds:
4498 if ( !trk->is_route &&
4499 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4500 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4502 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4503 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4508 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4511 trk_copy->is_route = !trk_copy->is_route;
4513 // ATM can't set name to self - so must create temporary copy
4514 gchar *name = g_strdup ( trk_copy->name );
4516 // Delete old one and then add new one
4517 if ( trk->is_route ) {
4518 vik_trw_layer_delete_route ( vtl, trk );
4519 vik_trw_layer_add_track ( vtl, name, trk_copy );
4522 // Extra route conversion bits...
4523 vik_track_merge_segments ( trk_copy );
4524 vik_track_to_routepoints ( trk_copy );
4526 vik_trw_layer_delete_track ( vtl, trk );
4527 vik_trw_layer_add_route ( vtl, name, trk_copy );
4531 // Update in case color of track / route changes when moving between sublayers
4532 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4536 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4538 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4540 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4541 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4543 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4548 vtl->current_track = track;
4549 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);
4551 if ( track->trackpoints )
4552 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4555 #ifdef VIK_CONFIG_GOOGLE
4557 * extend a track using route finder
4559 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4561 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4562 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4565 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4567 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4568 vtl->route_finder_coord = last_coord;
4569 vtl->route_finder_current_track = track;
4570 vtl->route_finder_started = TRUE;
4572 if ( track->trackpoints )
4573 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4578 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4580 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4581 /* Also warn if overwrite old elevation data */
4582 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4584 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4585 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4587 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4590 vik_track_apply_dem_data ( track );
4593 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4595 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4597 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4598 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4600 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4605 GList *trps = track->trackpoints;
4608 trps = g_list_last(trps);
4609 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4612 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4614 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4616 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4617 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4619 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4624 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4627 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4630 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4632 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4634 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4635 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4637 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4642 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4645 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4648 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4650 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4652 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4653 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4655 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4660 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4663 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4667 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4669 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4671 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4673 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4674 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4676 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4678 if ( trk && trk->trackpoints )
4680 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4681 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4682 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4683 if ( pass_along[1] )
4684 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4686 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4690 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4692 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4693 trw_layer_tpwin_init ( vtl );
4696 /*************************************
4697 * merge/split by time routines
4698 *************************************/
4700 /* called for each key in track hash table.
4701 * If the current track has the same time stamp type, add it to the result,
4702 * except the one pointed by "exclude".
4703 * set exclude to NULL if there is no exclude to check.
4704 * Note that the result is in reverse (for performance reasons).
4709 gboolean with_timestamps;
4711 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4713 twt_udata *user_data = udata;
4714 VikTrackpoint *p1, *p2;
4716 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4720 if (VIK_TRACK(value)->trackpoints) {
4721 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4722 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4724 if ( user_data->with_timestamps ) {
4725 if (!p1->has_timestamp || !p2->has_timestamp) {
4730 // Don't add tracks with timestamps when getting non timestamp tracks
4731 if (p1->has_timestamp || p2->has_timestamp) {
4737 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4740 /* called for each key in track hash table. if original track user_data[1] is close enough
4741 * to the passed one, add it to list in user_data[0]
4743 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4746 VikTrackpoint *p1, *p2;
4747 VikTrack *trk = VIK_TRACK(value);
4749 GList **nearby_tracks = ((gpointer *)user_data)[0];
4750 GList *tpoints = ((gpointer *)user_data)[1];
4753 * detect reasons for not merging, and return
4754 * if no reason is found not to merge, then do it.
4757 // Exclude the original track from the compiled list
4758 if (trk->trackpoints == tpoints) {
4762 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4763 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4765 if (trk->trackpoints) {
4766 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4767 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4769 if (!p1->has_timestamp || !p2->has_timestamp) {
4770 //g_print("no timestamp\n");
4774 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4775 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4776 if (! (abs(t1 - p2->timestamp) < threshold ||
4778 abs(p1->timestamp - t2) < threshold)
4785 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4788 /* comparison function used to sort tracks; a and b are hash table keys */
4789 /* Not actively used - can be restored if needed
4790 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4792 GHashTable *tracks = user_data;
4795 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4796 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4798 if (t1 < t2) return -1;
4799 if (t1 > t2) return 1;
4804 /* comparison function used to sort trackpoints */
4805 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4807 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4809 if (t1 < t2) return -1;
4810 if (t1 > t2) return 1;
4815 * comparison function which can be used to sort tracks or waypoints by name
4817 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4819 const gchar* namea = (const gchar*) a;
4820 const gchar* nameb = (const gchar*) b;
4821 if ( namea == NULL || nameb == NULL)
4824 // Same sort method as used in the vik_treeview_*_alphabetize functions
4825 return strcmp ( namea, nameb );
4829 * Attempt to merge selected track with other tracks specified by the user
4830 * Tracks to merge with must be of the same 'type' as the selected track -
4831 * either all with timestamps, or all without timestamps
4833 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4835 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4836 GList *other_tracks = NULL;
4837 GHashTable *ght_tracks;
4838 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4839 ght_tracks = vtl->routes;
4841 ght_tracks = vtl->tracks;
4843 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4848 if ( !track->trackpoints )
4852 udata.result = &other_tracks;
4853 udata.exclude = track->trackpoints;
4854 // Allow merging with 'similar' time type time tracks
4855 // i.e. either those times, or those without
4856 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4858 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4859 other_tracks = g_list_reverse(other_tracks);
4861 if ( !other_tracks ) {
4862 if ( udata.with_timestamps )
4863 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4865 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4869 // Sort alphabetically for user presentation
4870 // Convert into list of names for usage with dialog function
4871 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4872 GList *other_tracks_names = NULL;
4873 GList *iter = g_list_first ( other_tracks );
4875 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4876 iter = g_list_next ( iter );
4879 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4881 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4885 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4886 g_list_free(other_tracks);
4887 g_list_free(other_tracks_names);
4892 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4893 VikTrack *merge_track;
4894 if ( track->is_route )
4895 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4897 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4900 vik_track_steal_and_append_trackpoints ( track, merge_track );
4901 if ( track->is_route )
4902 vik_trw_layer_delete_route (vtl, merge_track);
4904 vik_trw_layer_delete_track (vtl, merge_track);
4905 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4908 for (l = merge_list; l != NULL; l = g_list_next(l))
4910 g_list_free(merge_list);
4912 vik_layer_emit_update( VIK_LAYER(vtl) );
4916 // c.f. trw_layer_sorted_track_id_by_name_list
4917 // but don't add the specified track to the list (normally current track)
4918 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4920 twt_udata *user_data = udata;
4923 if (trk->trackpoints == user_data->exclude) {
4927 // Sort named list alphabetically
4928 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4932 * Join - this allows combining 'tracks' and 'track routes'
4933 * i.e. doesn't care about whether tracks have consistent timestamps
4934 * ATM can only append one track at a time to the currently selected track
4936 static void trw_layer_append_track ( gpointer pass_along[6] )
4939 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4941 GHashTable *ght_tracks;
4942 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4943 ght_tracks = vtl->routes;
4945 ght_tracks = vtl->tracks;
4947 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4952 GList *other_tracks_names = NULL;
4954 // Sort alphabetically for user presentation
4955 // Convert into list of names for usage with dialog function
4956 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4958 udata.result = &other_tracks_names;
4959 udata.exclude = trk->trackpoints;
4961 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4963 // Note the limit to selecting one track only
4964 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4965 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4966 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4969 trk->is_route ? _("Append Route"): _("Append Track"),
4970 trk->is_route ? _("Select the route to append after the current route") :
4971 _("Select the track to append after the current track") );
4973 g_list_free(other_tracks_names);
4975 // It's a list, but shouldn't contain more than one other track!
4976 if ( append_list ) {
4978 for (l = append_list; l != NULL; l = g_list_next(l)) {
4979 // TODO: at present this uses the first track found by name,
4980 // which with potential multiple same named tracks may not be the one selected...
4981 VikTrack *append_track;
4982 if ( trk->is_route )
4983 append_track = vik_trw_layer_get_route ( vtl, l->data );
4985 append_track = vik_trw_layer_get_track ( vtl, l->data );
4987 if ( append_track ) {
4988 vik_track_steal_and_append_trackpoints ( trk, append_track );
4989 if ( trk->is_route )
4990 vik_trw_layer_delete_route (vtl, append_track);
4992 vik_trw_layer_delete_track (vtl, append_track);
4995 for (l = append_list; l != NULL; l = g_list_next(l))
4997 g_list_free(append_list);
4999 vik_layer_emit_update( VIK_LAYER(vtl) );
5004 * Very similar to trw_layer_append_track for joining
5005 * but this allows selection from the 'other' list
5006 * If a track is selected, then is shows routes and joins the selected one
5007 * If a route is selected, then is shows tracks and joins the selected one
5009 static void trw_layer_append_other ( gpointer pass_along[6] )
5012 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5014 GHashTable *ght_mykind, *ght_others;
5015 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5016 ght_mykind = vtl->routes;
5017 ght_others = vtl->tracks;
5020 ght_mykind = vtl->tracks;
5021 ght_others = vtl->routes;
5024 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5029 GList *other_tracks_names = NULL;
5031 // Sort alphabetically for user presentation
5032 // Convert into list of names for usage with dialog function
5033 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5035 udata.result = &other_tracks_names;
5036 udata.exclude = trk->trackpoints;
5038 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5040 // Note the limit to selecting one track only
5041 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5042 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5043 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5046 trk->is_route ? _("Append Track"): _("Append Route"),
5047 trk->is_route ? _("Select the track to append after the current route") :
5048 _("Select the route to append after the current track") );
5050 g_list_free(other_tracks_names);
5052 // It's a list, but shouldn't contain more than one other track!
5053 if ( append_list ) {
5055 for (l = append_list; l != NULL; l = g_list_next(l)) {
5056 // TODO: at present this uses the first track found by name,
5057 // which with potential multiple same named tracks may not be the one selected...
5059 // Get FROM THE OTHER TYPE list
5060 VikTrack *append_track;
5061 if ( trk->is_route )
5062 append_track = vik_trw_layer_get_track ( vtl, l->data );
5064 append_track = vik_trw_layer_get_route ( vtl, l->data );
5066 if ( append_track ) {
5068 if ( !append_track->is_route &&
5069 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5070 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5072 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5073 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5074 vik_track_merge_segments ( append_track );
5075 vik_track_to_routepoints ( append_track );
5082 vik_track_steal_and_append_trackpoints ( trk, append_track );
5084 // Delete copied which is FROM THE OTHER TYPE list
5085 if ( trk->is_route )
5086 vik_trw_layer_delete_track (vtl, append_track);
5088 vik_trw_layer_delete_route (vtl, append_track);
5091 for (l = append_list; l != NULL; l = g_list_next(l))
5093 g_list_free(append_list);
5094 vik_layer_emit_update( VIK_LAYER(vtl) );
5098 /* merge by segments */
5099 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5101 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5102 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5103 guint segments = vik_track_merge_segments ( trk );
5104 // NB currently no need to redraw as segments not actually shown on the display
5105 // However inform the user of what happened:
5107 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5108 g_snprintf(str, 64, tmp_str, segments);
5109 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5112 /* merge by time routine */
5113 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5115 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5119 GList *tracks_with_timestamp = NULL;
5120 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5121 if (orig_trk->trackpoints &&
5122 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5123 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5128 udata.result = &tracks_with_timestamp;
5129 udata.exclude = orig_trk->trackpoints;
5130 udata.with_timestamps = TRUE;
5131 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5132 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5134 if (!tracks_with_timestamp) {
5135 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5138 g_list_free(tracks_with_timestamp);
5140 static guint threshold_in_minutes = 1;
5141 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5142 _("Merge Threshold..."),
5143 _("Merge when time between tracks less than:"),
5144 &threshold_in_minutes)) {
5148 // keep attempting to merge all tracks until no merges within the time specified is possible
5149 gboolean attempt_merge = TRUE;
5150 GList *nearby_tracks = NULL;
5152 static gpointer params[3];
5154 while ( attempt_merge ) {
5156 // Don't try again unless tracks have changed
5157 attempt_merge = FALSE;
5159 trps = orig_trk->trackpoints;
5163 if (nearby_tracks) {
5164 g_list_free(nearby_tracks);
5165 nearby_tracks = NULL;
5168 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5169 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5171 /* g_print("Original track times: %d and %d\n", t1, t2); */
5172 params[0] = &nearby_tracks;
5173 params[1] = (gpointer)trps;
5174 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5176 /* get a list of adjacent-in-time tracks */
5177 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5180 GList *l = nearby_tracks;
5183 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5184 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5186 t1 = get_first_trackpoint(l)->timestamp;
5187 t2 = get_last_trackpoint(l)->timestamp;
5188 #undef get_first_trackpoint
5189 #undef get_last_trackpoint
5190 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5193 /* remove trackpoints from merged track, delete track */
5194 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5195 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5197 // Tracks have changed, therefore retry again against all the remaining tracks
5198 attempt_merge = TRUE;
5203 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5206 g_list_free(nearby_tracks);
5208 vik_layer_emit_update( VIK_LAYER(vtl) );
5212 * Split a track at the currently selected trackpoint
5214 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5216 if ( !vtl->current_tpl )
5219 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5220 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5222 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5223 GList *newglist = g_list_alloc ();
5224 newglist->prev = NULL;
5225 newglist->next = vtl->current_tpl->next;
5226 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5227 tr->trackpoints = newglist;
5229 vtl->current_tpl->next->prev = newglist; /* end old track here */
5230 vtl->current_tpl->next = NULL;
5232 // Bounds of the selected track changed due to the split
5233 vik_track_calculate_bounds ( vtl->current_tp_track );
5235 vtl->current_tpl = newglist; /* change tp to first of new track. */
5236 vtl->current_tp_track = tr;
5239 vik_trw_layer_add_route ( vtl, name, tr );
5241 vik_trw_layer_add_track ( vtl, name, tr );
5243 // Bounds of the new track created by the split
5244 vik_track_calculate_bounds ( tr );
5250 // Also need id of newly created track
5253 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5255 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5257 if ( trkf && udata.uuid )
5258 vtl->current_tp_id = udata.uuid;
5260 vtl->current_tp_id = NULL;
5262 vik_layer_emit_update(VIK_LAYER(vtl));
5267 /* split by time routine */
5268 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5270 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5271 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5272 GList *trps = track->trackpoints;
5274 GList *newlists = NULL;
5275 GList *newtps = NULL;
5276 static guint thr = 1;
5283 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5284 _("Split Threshold..."),
5285 _("Split when time between trackpoints exceeds:"),
5290 /* iterate through trackpoints, and copy them into new lists without touching original list */
5291 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5295 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5297 g_print("panic: ts < prev_ts: this should never happen!\n");
5300 if (ts - prev_ts > thr*60) {
5301 /* flush accumulated trackpoints into new list */
5302 newlists = g_list_append(newlists, g_list_reverse(newtps));
5306 /* accumulate trackpoint copies in newtps, in reverse order */
5307 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5309 iter = g_list_next(iter);
5312 newlists = g_list_append(newlists, g_list_reverse(newtps));
5315 /* put lists of trackpoints into tracks */
5317 // Only bother updating if the split results in new tracks
5318 if (g_list_length (newlists) > 1) {
5323 tr = vik_track_copy ( track, FALSE );
5324 tr->trackpoints = (GList *)(iter->data);
5326 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5327 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5328 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5329 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5330 vik_track_calculate_bounds ( tr );
5332 iter = g_list_next(iter);
5334 // Remove original track and then update the display
5335 vik_trw_layer_delete_track (vtl, track);
5336 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5338 g_list_free(newlists);
5342 * Split a track by the number of points as specified by the user
5344 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5346 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5348 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5349 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5351 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5356 // Check valid track
5357 GList *trps = track->trackpoints;
5361 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5362 _("Split Every Nth Point"),
5363 _("Split on every Nth point:"),
5364 250, // Default value as per typical limited track capacity of various GPS devices
5368 // Was a valid number returned?
5374 GList *newlists = NULL;
5375 GList *newtps = NULL;
5380 /* accumulate trackpoint copies in newtps, in reverse order */
5381 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5383 if (count >= points) {
5384 /* flush accumulated trackpoints into new list */
5385 newlists = g_list_append(newlists, g_list_reverse(newtps));
5389 iter = g_list_next(iter);
5392 // If there is a remaining chunk put that into the new split list
5393 // This may well be the whole track if no split points were encountered
5395 newlists = g_list_append(newlists, g_list_reverse(newtps));
5398 /* put lists of trackpoints into tracks */
5400 // Only bother updating if the split results in new tracks
5401 if (g_list_length (newlists) > 1) {
5406 tr = vik_track_copy ( track, FALSE );
5407 tr->trackpoints = (GList *)(iter->data);
5409 if ( track->is_route ) {
5410 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5411 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5414 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5415 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5417 vik_track_calculate_bounds ( tr );
5419 iter = g_list_next(iter);
5421 // Remove original track and then update the display
5422 if ( track->is_route )
5423 vik_trw_layer_delete_route (vtl, track);
5425 vik_trw_layer_delete_track (vtl, track);
5426 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5428 g_list_free(newlists);
5432 * Split a track at the currently selected trackpoint
5434 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5436 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5437 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5438 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5442 * Split a track by its segments
5443 * Routes do not have segments so don't call this for routes
5445 static void trw_layer_split_segments ( gpointer pass_along[6] )
5447 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5448 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5455 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5458 for ( i = 0; i < ntracks; i++ ) {
5460 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5461 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5466 // Remove original track
5467 vik_trw_layer_delete_track ( vtl, trk );
5468 vik_layer_emit_update ( VIK_LAYER(vtl) );
5471 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5474 /* end of split/merge routines */
5477 * Delete adjacent track points at the same position
5478 * AKA Delete Dulplicates on the Properties Window
5480 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5482 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5484 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5485 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5487 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5492 gulong removed = vik_track_remove_dup_points ( trk );
5494 // Track has been updated so update tps:
5495 trw_layer_cancel_tps_of_track ( vtl, trk );
5497 // Inform user how much was deleted as it's not obvious from the normal view
5499 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5500 g_snprintf(str, 64, tmp_str, removed);
5501 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5503 vik_layer_emit_update ( VIK_LAYER(vtl) );
5507 * Delete adjacent track points with the same timestamp
5508 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5510 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5512 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5514 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5515 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5517 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5522 gulong removed = vik_track_remove_same_time_points ( trk );
5524 // Track has been updated so update tps:
5525 trw_layer_cancel_tps_of_track ( vtl, trk );
5527 // Inform user how much was deleted as it's not obvious from the normal view
5529 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5530 g_snprintf(str, 64, tmp_str, removed);
5531 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5533 vik_layer_emit_update ( VIK_LAYER(vtl) );
5539 static void trw_layer_reverse ( gpointer pass_along[6] )
5541 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5543 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5544 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5546 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5551 vik_track_reverse ( track );
5553 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5557 * Similar to trw_layer_enum_item, but this uses a sorted method
5560 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5562 GList **list = (GList**)udata;
5563 // *list = g_list_prepend(*all, key); //unsorted method
5564 // Sort named list alphabetically
5565 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5570 * Now Waypoint specific sort
5572 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5574 GList **list = (GList**)udata;
5575 // Sort named list alphabetically
5576 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5580 * Track specific sort
5582 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5584 GList **list = (GList**)udata;
5585 // Sort named list alphabetically
5586 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5591 gboolean has_same_track_name;
5592 const gchar *same_track_name;
5593 } same_track_name_udata;
5595 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5597 const gchar* namea = (const gchar*) aa;
5598 const gchar* nameb = (const gchar*) bb;
5601 gint result = strcmp ( namea, nameb );
5603 if ( result == 0 ) {
5604 // Found two names the same
5605 same_track_name_udata *user_data = udata;
5606 user_data->has_same_track_name = TRUE;
5607 user_data->same_track_name = namea;
5610 // Leave ordering the same
5615 * Find out if any tracks have the same name in this hash table
5617 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5619 // Sort items by name, then compare if any next to each other are the same
5621 GList *track_names = NULL;
5622 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5625 if ( ! track_names )
5628 same_track_name_udata udata;
5629 udata.has_same_track_name = FALSE;
5631 // Use sort routine to traverse list comparing items
5632 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5633 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5634 // Still no tracks...
5638 return udata.has_same_track_name;
5642 * Force unqiue track names for the track table specified
5643 * Note the panel is a required parameter to enable the update of the names displayed
5644 * Specify if on tracks or else on routes
5646 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5648 // . Search list for an instance of repeated name
5649 // . get track of this name
5650 // . create new name
5651 // . rename track & update equiv. treeview iter
5652 // . repeat until all different
5654 same_track_name_udata udata;
5656 GList *track_names = NULL;
5657 udata.has_same_track_name = FALSE;
5658 udata.same_track_name = NULL;
5660 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5663 if ( ! track_names )
5666 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5668 // Still no tracks...
5669 if ( ! dummy_list1 )
5672 while ( udata.has_same_track_name ) {
5674 // Find a track with the same name
5677 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5679 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5683 g_critical("Houston, we've had a problem.");
5684 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5685 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5690 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5691 vik_track_set_name ( trk, newname );
5697 // Need want key of it for treeview update
5698 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5700 if ( trkf && udataU.uuid ) {
5704 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5706 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5709 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5710 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5711 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5716 // Start trying to find same names again...
5718 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5719 udata.has_same_track_name = FALSE;
5720 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5722 // No tracks any more - give up searching
5723 if ( ! dummy_list2 )
5724 udata.has_same_track_name = FALSE;
5728 vik_layers_panel_emit_update ( vlp );
5734 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5736 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5739 // Ensure list of track names offered is unique
5740 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5741 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5742 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5743 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5749 // Sort list alphabetically for better presentation
5750 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5753 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5757 // Get list of items to delete from the user
5758 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5761 _("Delete Selection"),
5762 _("Select tracks to delete"));
5765 // Delete requested tracks
5766 // since specificly requested, IMHO no need for extra confirmation
5767 if ( delete_list ) {
5769 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5770 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5771 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5773 g_list_free(delete_list);
5774 vik_layer_emit_update( VIK_LAYER(vtl) );
5781 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5783 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5786 // Ensure list of track names offered is unique
5787 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5788 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5789 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5790 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5796 // Sort list alphabetically for better presentation
5797 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5800 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5804 // Get list of items to delete from the user
5805 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5808 _("Delete Selection"),
5809 _("Select routes to delete") );
5812 // Delete requested routes
5813 // since specificly requested, IMHO no need for extra confirmation
5814 if ( delete_list ) {
5816 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5817 // This deletes first route it finds of that name (but uniqueness is enforced above)
5818 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5820 g_list_free(delete_list);
5821 vik_layer_emit_update( VIK_LAYER(vtl) );
5826 gboolean has_same_waypoint_name;
5827 const gchar *same_waypoint_name;
5828 } same_waypoint_name_udata;
5830 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5832 const gchar* namea = (const gchar*) aa;
5833 const gchar* nameb = (const gchar*) bb;
5836 gint result = strcmp ( namea, nameb );
5838 if ( result == 0 ) {
5839 // Found two names the same
5840 same_waypoint_name_udata *user_data = udata;
5841 user_data->has_same_waypoint_name = TRUE;
5842 user_data->same_waypoint_name = namea;
5845 // Leave ordering the same
5850 * Find out if any waypoints have the same name in this layer
5852 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5854 // Sort items by name, then compare if any next to each other are the same
5856 GList *waypoint_names = NULL;
5857 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5860 if ( ! waypoint_names )
5863 same_waypoint_name_udata udata;
5864 udata.has_same_waypoint_name = FALSE;
5866 // Use sort routine to traverse list comparing items
5867 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5868 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5869 // Still no waypoints...
5873 return udata.has_same_waypoint_name;
5877 * Force unqiue waypoint names for this layer
5878 * Note the panel is a required parameter to enable the update of the names displayed
5880 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5882 // . Search list for an instance of repeated name
5883 // . get waypoint of this name
5884 // . create new name
5885 // . rename waypoint & update equiv. treeview iter
5886 // . repeat until all different
5888 same_waypoint_name_udata udata;
5890 GList *waypoint_names = NULL;
5891 udata.has_same_waypoint_name = FALSE;
5892 udata.same_waypoint_name = NULL;
5894 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5897 if ( ! waypoint_names )
5900 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5902 // Still no waypoints...
5903 if ( ! dummy_list1 )
5906 while ( udata.has_same_waypoint_name ) {
5908 // Find a waypoint with the same name
5909 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5913 g_critical("Houston, we've had a problem.");
5914 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5915 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5920 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5922 trw_layer_waypoint_rename ( vtl, waypoint, newname );
5924 // Start trying to find same names again...
5925 waypoint_names = NULL;
5926 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5927 udata.has_same_waypoint_name = FALSE;
5928 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5930 // No waypoints any more - give up searching
5931 if ( ! dummy_list2 )
5932 udata.has_same_waypoint_name = FALSE;
5936 vik_layers_panel_emit_update ( vlp );
5942 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5944 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5947 // Ensure list of waypoint names offered is unique
5948 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5949 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5950 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5951 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5957 // Sort list alphabetically for better presentation
5958 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5960 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5964 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5966 // Get list of items to delete from the user
5967 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5970 _("Delete Selection"),
5971 _("Select waypoints to delete"));
5974 // Delete requested waypoints
5975 // since specificly requested, IMHO no need for extra confirmation
5976 if ( delete_list ) {
5978 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5979 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5980 trw_layer_delete_waypoint_by_name (vtl, l->data);
5982 g_list_free(delete_list);
5984 trw_layer_calculate_bounds_waypoints ( vtl );
5985 vik_layer_emit_update( VIK_LAYER(vtl) );
5990 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5992 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5994 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5997 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5999 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6002 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6003 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6007 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6009 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6012 if ( !strncmp(wp->comment, "http", 4) ) {
6013 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6014 } else if ( !strncmp(wp->description, "http", 4) ) {
6015 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6019 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6021 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6023 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6025 // No actual change to the name supplied
6027 if (strcmp(newname, wp->name) == 0 )
6030 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6033 // An existing waypoint has been found with the requested name
6034 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6035 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6040 // Update WP name and refresh the treeview
6041 vik_waypoint_set_name (wp, newname);
6043 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6044 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6047 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6052 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6054 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6056 // No actual change to the name supplied
6058 if (strcmp(newname, trk->name) == 0)
6061 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6064 // An existing track has been found with the requested name
6065 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6066 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6070 // Update track name and refresh GUI parts
6071 vik_track_set_name (trk, newname);
6073 // Update any subwindows that could be displaying this track which has changed name
6074 // Only one Track Edit Window
6075 if ( l->current_tp_track == trk && l->tpwin ) {
6076 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6078 // Property Dialog of the track
6079 vik_trw_layer_propwin_update ( trk );
6081 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6082 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6085 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6090 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6092 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6094 // No actual change to the name supplied
6096 if (strcmp(newname, trk->name) == 0)
6099 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6102 // An existing track has been found with the requested name
6103 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6104 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6108 // Update track name and refresh GUI parts
6109 vik_track_set_name (trk, newname);
6111 // Update any subwindows that could be displaying this track which has changed name
6112 // Only one Track Edit Window
6113 if ( l->current_tp_track == trk && l->tpwin ) {
6114 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6116 // Property Dialog of the track
6117 vik_trw_layer_propwin_update ( trk );
6119 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6120 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6123 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6130 static gboolean is_valid_geocache_name ( gchar *str )
6132 gint len = strlen ( str );
6133 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]));
6136 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6138 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6139 a_acquire_set_filter_track ( trk );
6142 #ifdef VIK_CONFIG_GOOGLE
6143 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6145 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6146 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6149 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6151 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6153 gchar *escaped = uri_escape ( tr->comment );
6154 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6155 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6162 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6163 /* viewpoint is now available instead */
6164 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6166 static gpointer pass_along[8];
6168 gboolean rv = FALSE;
6171 pass_along[1] = vlp;
6172 pass_along[2] = GINT_TO_POINTER (subtype);
6173 pass_along[3] = sublayer;
6174 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6175 pass_along[5] = vvp;
6176 pass_along[6] = iter;
6177 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6179 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6183 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6185 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6186 gtk_widget_show ( item );
6188 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6189 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6190 if (tr && tr->property_dialog)
6191 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6193 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6194 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6195 if (tr && tr->property_dialog)
6196 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6199 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6201 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6202 gtk_widget_show ( item );
6204 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6205 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6206 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6207 gtk_widget_show ( item );
6209 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6210 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6211 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6212 gtk_widget_show ( item );
6214 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6216 gboolean separator_created = FALSE;
6218 /* could be a right-click using the tool */
6219 if ( vlp != NULL ) {
6220 item = gtk_menu_item_new ();
6221 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6222 gtk_widget_show ( item );
6224 separator_created = TRUE;
6226 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6227 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6228 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6229 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6230 gtk_widget_show ( item );
6233 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6235 if ( wp && wp->name ) {
6236 if ( is_valid_geocache_name ( wp->name ) ) {
6238 if ( !separator_created ) {
6239 item = gtk_menu_item_new ();
6240 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6241 gtk_widget_show ( item );
6242 separator_created = TRUE;
6245 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6246 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6247 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6248 gtk_widget_show ( item );
6252 if ( wp && wp->image )
6254 if ( !separator_created ) {
6255 item = gtk_menu_item_new ();
6256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6257 gtk_widget_show ( item );
6258 separator_created = TRUE;
6261 // Set up image paramater
6262 pass_along[5] = wp->image;
6264 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6265 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6266 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6267 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6268 gtk_widget_show ( item );
6270 #ifdef VIK_CONFIG_GEOTAG
6271 GtkWidget *geotag_submenu = gtk_menu_new ();
6272 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6273 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6274 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6275 gtk_widget_show ( item );
6276 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6278 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6280 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6281 gtk_widget_show ( item );
6283 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6285 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6286 gtk_widget_show ( item );
6292 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6293 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6294 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6295 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6297 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6298 gtk_widget_show ( item );
6305 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6306 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6307 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6308 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6309 gtk_widget_show ( item );
6310 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6311 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6312 gtk_widget_set_sensitive ( item, TRUE );
6314 gtk_widget_set_sensitive ( item, FALSE );
6317 item = gtk_menu_item_new ();
6318 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6319 gtk_widget_show ( item );
6322 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6325 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6326 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6328 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6329 gtk_widget_show ( item );
6332 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6334 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6335 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6337 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6338 gtk_widget_show ( item );
6340 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6341 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6343 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6344 gtk_widget_show ( item );
6346 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6347 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6348 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6349 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6350 gtk_widget_show ( item );
6352 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6353 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6354 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6355 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6356 gtk_widget_show ( item );
6359 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6363 if ( l->current_track && !l->current_track->is_route ) {
6364 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6365 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6366 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6367 gtk_widget_show ( item );
6369 item = gtk_menu_item_new ();
6370 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6371 gtk_widget_show ( item );
6374 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6375 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6376 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6377 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6378 gtk_widget_show ( item );
6380 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6381 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6382 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6383 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6384 gtk_widget_show ( item );
6385 // Make it available only when a new track *not* already in progress
6386 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6388 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6389 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6390 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6391 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6392 gtk_widget_show ( item );
6394 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6395 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6396 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6397 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6398 gtk_widget_show ( item );
6401 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6405 if ( l->current_track && l->current_track->is_route ) {
6406 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6407 // Reuse finish track method
6408 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6409 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6410 gtk_widget_show ( item );
6412 item = gtk_menu_item_new ();
6413 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6414 gtk_widget_show ( item );
6417 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6418 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6420 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6421 gtk_widget_show ( item );
6423 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6424 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6425 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6426 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6427 gtk_widget_show ( item );
6428 // Make it available only when a new track *not* already in progress
6429 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6431 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6432 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6433 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6434 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6435 gtk_widget_show ( item );
6437 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6438 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6439 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6440 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6441 gtk_widget_show ( item );
6444 GtkWidget *upload_submenu = gtk_menu_new ();
6446 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6448 item = gtk_menu_item_new ();
6449 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6450 gtk_widget_show ( item );
6452 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6453 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6454 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6455 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6456 if ( l->current_track ) {
6457 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6458 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6459 gtk_widget_show ( item );
6462 item = gtk_menu_item_new ();
6463 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6464 gtk_widget_show ( item );
6467 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6468 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6470 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6471 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6473 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6474 gtk_widget_show ( item );
6476 GtkWidget *goto_submenu;
6477 goto_submenu = gtk_menu_new ();
6478 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6479 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6480 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6481 gtk_widget_show ( item );
6482 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6484 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6485 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6486 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6487 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6488 gtk_widget_show ( item );
6490 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6491 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6493 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6494 gtk_widget_show ( item );
6496 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6497 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6499 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6500 gtk_widget_show ( item );
6502 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6503 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6505 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6506 gtk_widget_show ( item );
6508 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6509 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6511 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6512 gtk_widget_show ( item );
6514 // Routes don't have speeds
6515 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6516 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6517 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6519 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6520 gtk_widget_show ( item );
6523 GtkWidget *combine_submenu;
6524 combine_submenu = gtk_menu_new ();
6525 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6526 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6527 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6528 gtk_widget_show ( item );
6529 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6531 // Routes don't have times or segments...
6532 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6533 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6535 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6536 gtk_widget_show ( item );
6538 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6539 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6540 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6541 gtk_widget_show ( item );
6544 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6546 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6547 gtk_widget_show ( item );
6549 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6550 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6552 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6553 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6554 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6555 gtk_widget_show ( item );
6557 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6558 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6560 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6562 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6563 gtk_widget_show ( item );
6565 GtkWidget *split_submenu;
6566 split_submenu = gtk_menu_new ();
6567 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6568 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6569 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6570 gtk_widget_show ( item );
6571 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6573 // Routes don't have times or segments...
6574 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6575 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6576 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6577 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6578 gtk_widget_show ( item );
6580 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6581 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6582 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6583 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6584 gtk_widget_show ( item );
6587 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6588 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6589 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6590 gtk_widget_show ( item );
6592 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6593 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6594 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6595 gtk_widget_show ( item );
6596 // Make it available only when a trackpoint is selected.
6597 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6599 GtkWidget *delete_submenu;
6600 delete_submenu = gtk_menu_new ();
6601 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6602 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6603 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6604 gtk_widget_show ( item );
6605 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6607 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6608 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6609 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6610 gtk_widget_show ( item );
6612 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6613 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6614 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6615 gtk_widget_show ( item );
6617 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6618 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6620 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6621 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6623 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6624 gtk_widget_show ( item );
6626 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6628 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6629 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6631 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6632 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6633 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6634 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6635 gtk_widget_show ( item );
6638 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6639 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("DEM Download/Import", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6640 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6641 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6642 gtk_widget_show ( item );
6644 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6645 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6647 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6648 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6649 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6650 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6651 gtk_widget_show ( item );
6653 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6654 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6656 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6657 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6658 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6659 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6660 gtk_widget_show ( item );
6662 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6663 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6665 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6666 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6667 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6668 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6669 gtk_widget_show ( item );
6671 #ifdef VIK_CONFIG_GOOGLE
6672 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6673 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6674 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6675 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6676 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6677 gtk_widget_show ( item );
6681 // ATM can't upload a single waypoint but can do waypoints to a GPS
6682 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6683 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6684 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6685 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6686 gtk_widget_show ( item );
6687 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6689 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6690 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6691 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6692 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6693 gtk_widget_show ( item );
6697 #ifdef VIK_CONFIG_GOOGLE
6698 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6700 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6701 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6702 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6703 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6704 gtk_widget_show ( item );
6708 // Some things aren't usable with routes
6709 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6710 #ifdef VIK_CONFIG_OPENSTREETMAP
6711 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6712 // Convert internal pointer into actual track for usage outside this file
6713 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6716 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6717 gtk_widget_show ( item );
6720 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6721 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6722 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6723 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6724 gtk_widget_show ( item );
6726 /* ATM This function is only available via the layers panel, due to needing a vlp */
6728 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6729 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6730 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6732 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6733 gtk_widget_show ( item );
6737 #ifdef VIK_CONFIG_GEOTAG
6738 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6739 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6740 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6741 gtk_widget_show ( item );
6745 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6746 // Only show on viewport popmenu when a trackpoint is selected
6747 if ( ! vlp && l->current_tpl ) {
6749 item = gtk_menu_item_new ();
6750 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6751 gtk_widget_show ( item );
6753 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6754 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6755 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6756 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6757 gtk_widget_show ( item );
6764 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6767 if (!vtl->current_tpl)
6769 if (!vtl->current_tpl->next)
6772 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6773 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6775 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6778 VikTrackpoint *tp_new = vik_trackpoint_new();
6779 struct LatLon ll_current, ll_next;
6780 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6781 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6783 /* main positional interpolation */
6784 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6785 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6787 /* Now other properties that can be interpolated */
6788 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6790 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6791 /* Note here the division is applied to each part, then added
6792 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6793 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6794 tp_new->has_timestamp = TRUE;
6797 if (tp_current->speed != NAN && tp_next->speed != NAN)
6798 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6800 /* TODO - improve interpolation of course, as it may not be correct.
6801 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6802 [similar applies if value is in radians] */
6803 if (tp_current->course != NAN && tp_next->course != NAN)
6804 tp_new->speed = (tp_current->course + tp_next->course)/2;
6806 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6808 /* Insert new point into the trackpoints list after the current TP */
6809 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6811 // Otherwise try routes
6812 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6816 gint index = g_list_index ( trk->trackpoints, tp_current );
6818 // NB no recalculation of bounds since it is inserted between points
6819 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6824 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6830 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6834 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6836 if ( vtl->current_tpl )
6838 vtl->current_tpl = NULL;
6839 vtl->current_tp_track = NULL;
6840 vtl->current_tp_id = NULL;
6841 vik_layer_emit_update(VIK_LAYER(vtl));
6845 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6847 g_assert ( vtl->tpwin != NULL );
6848 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6849 trw_layer_cancel_current_tp ( vtl, TRUE );
6851 if ( vtl->current_tpl == NULL )
6854 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6856 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6857 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6859 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6861 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6863 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6869 // Find available adjacent trackpoint
6870 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6872 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6873 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6875 // Delete current trackpoint
6876 vik_trackpoint_free ( vtl->current_tpl->data );
6877 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6879 // Set to current to the available adjacent trackpoint
6880 vtl->current_tpl = new_tpl;
6882 // Reset dialog with the available adjacent trackpoint
6883 if ( vtl->current_tp_track ) {
6884 vik_track_calculate_bounds ( vtl->current_tp_track );
6885 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6888 vik_layer_emit_update(VIK_LAYER(vtl));
6892 // Delete current trackpoint
6893 vik_trackpoint_free ( vtl->current_tpl->data );
6894 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6895 trw_layer_cancel_current_tp ( vtl, FALSE );
6898 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6900 if ( vtl->current_tp_track )
6901 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6902 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
6904 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6906 if ( vtl->current_tp_track )
6907 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6908 vik_layer_emit_update(VIK_LAYER(vtl));
6910 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6912 trw_layer_insert_tp_after_current_tp ( vtl );
6913 vik_layer_emit_update(VIK_LAYER(vtl));
6915 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6916 vik_layer_emit_update(VIK_LAYER(vtl));
6920 * trw_layer_dialog_shift:
6921 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
6923 * Try to reposition a dialog if it's over the specified coord
6924 * so to not obscure the item of interest
6926 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
6928 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
6930 // get parent window position & size
6931 gint win_pos_x, win_pos_y;
6932 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
6934 gint win_size_x, win_size_y;
6935 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
6937 // get own dialog size
6938 gint dia_size_x, dia_size_y;
6939 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
6941 // get own dialog position
6942 gint dia_pos_x, dia_pos_y;
6943 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
6945 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
6946 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
6948 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
6950 gint vp_xx, vp_yy; // In viewport pixels
6951 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
6953 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
6957 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
6959 // Transform Viewport pixels into absolute pixels
6960 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
6961 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
6963 // Is dialog over the point (to within an ^^ edge value)
6964 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
6965 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
6969 gint hh = vik_viewport_get_height ( vvp );
6971 // Consider the difference in viewport to the full window
6972 gint offset_y = dest_y;
6973 // Add difference between dialog and window sizes
6974 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
6976 if ( vp_yy > hh/2 ) {
6977 // Point in bottom half, move window to top half
6978 gtk_window_move ( dialog, dia_pos_x, offset_y );
6981 // Point in top half, move dialog down
6982 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
6986 // Shift left<->right
6987 gint ww = vik_viewport_get_width ( vvp );
6989 // Consider the difference in viewport to the full window
6990 gint offset_x = dest_x;
6991 // Add difference between dialog and window sizes
6992 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
6994 if ( vp_xx > ww/2 ) {
6995 // Point on right, move window to left
6996 gtk_window_move ( dialog, offset_x, dia_pos_y );
6999 // Point on left, move right
7000 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7008 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7012 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7013 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7014 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7015 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7017 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7019 if ( vtl->current_tpl ) {
7020 // get tp pixel position
7021 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7023 // Shift up<->down to try not to obscure the trackpoint.
7024 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7028 if ( vtl->current_tpl )
7029 if ( vtl->current_tp_track )
7030 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7031 /* set layer name and TP data */
7034 /***************************************************************************
7036 ***************************************************************************/
7038 /*** Utility data structures and functions ****/
7042 gint closest_x, closest_y;
7043 gboolean draw_images;
7044 gpointer *closest_wp_id;
7045 VikWaypoint *closest_wp;
7051 gint closest_x, closest_y;
7052 gpointer closest_track_id;
7053 VikTrackpoint *closest_tp;
7059 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7065 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7067 // If waypoint has an image then use the image size to select
7068 if ( params->draw_images && wp->image ) {
7069 gint slackx, slacky;
7070 slackx = wp->image_width / 2;
7071 slacky = wp->image_height / 2;
7073 if ( x <= params->x + slackx && x >= params->x - slackx
7074 && y <= params->y + slacky && y >= params->y - slacky ) {
7075 params->closest_wp_id = id;
7076 params->closest_wp = wp;
7077 params->closest_x = x;
7078 params->closest_y = y;
7081 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7082 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7083 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7085 params->closest_wp_id = id;
7086 params->closest_wp = wp;
7087 params->closest_x = x;
7088 params->closest_y = y;
7092 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7094 GList *tpl = t->trackpoints;
7100 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7106 tp = VIK_TRACKPOINT(tpl->data);
7108 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7110 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7111 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7112 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7114 params->closest_track_id = id;
7115 params->closest_tp = tp;
7116 params->closest_tpl = tpl;
7117 params->closest_x = x;
7118 params->closest_y = y;
7124 // ATM: Leave this as 'Track' only.
7125 // Not overly bothered about having a snap to route trackpoint capability
7126 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7128 TPSearchParams params;
7132 params.closest_track_id = NULL;
7133 params.closest_tp = NULL;
7134 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7135 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7136 return params.closest_tp;
7139 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7141 WPSearchParams params;
7145 params.draw_images = vtl->drawimages;
7146 params.closest_wp = NULL;
7147 params.closest_wp_id = NULL;
7148 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7149 return params.closest_wp;
7153 // Some forward declarations
7154 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7155 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7156 static void marker_end_move ( tool_ed_t *t );
7159 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7163 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7165 // Here always allow snapping back to the original location
7166 // this is useful when one decides not to move the thing afterall
7167 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7170 if ( event->state & GDK_CONTROL_MASK )
7172 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7174 new_coord = tp->coord;
7178 if ( event->state & GDK_SHIFT_MASK )
7180 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7182 new_coord = wp->coord;
7186 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7188 marker_moveto ( t, x, y );
7195 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7197 if ( t->holding && event->button == 1 )
7200 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7203 if ( event->state & GDK_CONTROL_MASK )
7205 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7207 new_coord = tp->coord;
7211 if ( event->state & GDK_SHIFT_MASK )
7213 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7215 new_coord = wp->coord;
7218 marker_end_move ( t );
7220 // Determine if working on a waypoint or a trackpoint
7221 if ( t->is_waypoint ) {
7222 vtl->current_wp->coord = new_coord;
7223 trw_layer_calculate_bounds_waypoints ( vtl );
7226 if ( vtl->current_tpl ) {
7227 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7229 if ( vtl->current_tp_track )
7230 vik_track_calculate_bounds ( vtl->current_tp_track );
7233 if ( vtl->current_tp_track )
7234 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7239 vtl->current_wp = NULL;
7240 vtl->current_wp_id = NULL;
7241 trw_layer_cancel_current_tp ( vtl, FALSE );
7243 vik_layer_emit_update ( VIK_LAYER(vtl) );
7250 Returns true if a waypoint or track is found near the requested event position for this particular layer
7251 The item found is automatically selected
7252 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7254 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7256 if ( event->button != 1 )
7259 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7262 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7266 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7268 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7270 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7271 WPSearchParams wp_params;
7272 wp_params.vvp = vvp;
7273 wp_params.x = event->x;
7274 wp_params.y = event->y;
7275 wp_params.draw_images = vtl->drawimages;
7276 wp_params.closest_wp_id = NULL;
7277 wp_params.closest_wp = NULL;
7279 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7281 if ( wp_params.closest_wp ) {
7284 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7286 // Too easy to move it so must be holding shift to start immediately moving it
7287 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7288 if ( event->state & GDK_SHIFT_MASK ||
7289 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7290 // Put into 'move buffer'
7291 // NB vvp & vw already set in tet
7292 tet->vtl = (gpointer)vtl;
7293 tet->is_waypoint = TRUE;
7295 marker_begin_move (tet, event->x, event->y);
7298 vtl->current_wp = wp_params.closest_wp;
7299 vtl->current_wp_id = wp_params.closest_wp_id;
7301 vik_layer_emit_update ( VIK_LAYER(vtl) );
7307 // Used for both track and route lists
7308 TPSearchParams tp_params;
7309 tp_params.vvp = vvp;
7310 tp_params.x = event->x;
7311 tp_params.y = event->y;
7312 tp_params.closest_track_id = NULL;
7313 tp_params.closest_tp = NULL;
7314 tp_params.bbox = bbox;
7316 if (vtl->tracks_visible) {
7317 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7319 if ( tp_params.closest_tp ) {
7321 // Always select + highlight the track
7322 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7324 tet->is_waypoint = FALSE;
7326 // Select the Trackpoint
7327 // Can move it immediately when control held or it's the previously selected tp
7328 if ( event->state & GDK_CONTROL_MASK ||
7329 vtl->current_tpl == tp_params.closest_tpl ) {
7330 // Put into 'move buffer'
7331 // NB vvp & vw already set in tet
7332 tet->vtl = (gpointer)vtl;
7333 marker_begin_move (tet, event->x, event->y);
7336 vtl->current_tpl = tp_params.closest_tpl;
7337 vtl->current_tp_id = tp_params.closest_track_id;
7338 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7340 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7343 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7345 vik_layer_emit_update ( VIK_LAYER(vtl) );
7350 // Try again for routes
7351 if (vtl->routes_visible) {
7352 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7354 if ( tp_params.closest_tp ) {
7356 // Always select + highlight the track
7357 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7359 tet->is_waypoint = FALSE;
7361 // Select the Trackpoint
7362 // Can move it immediately when control held or it's the previously selected tp
7363 if ( event->state & GDK_CONTROL_MASK ||
7364 vtl->current_tpl == tp_params.closest_tpl ) {
7365 // Put into 'move buffer'
7366 // NB vvp & vw already set in tet
7367 tet->vtl = (gpointer)vtl;
7368 marker_begin_move (tet, event->x, event->y);
7371 vtl->current_tpl = tp_params.closest_tpl;
7372 vtl->current_tp_id = tp_params.closest_track_id;
7373 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7375 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7378 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7380 vik_layer_emit_update ( VIK_LAYER(vtl) );
7385 /* these aren't the droids you're looking for */
7386 vtl->current_wp = NULL;
7387 vtl->current_wp_id = NULL;
7388 trw_layer_cancel_current_tp ( vtl, FALSE );
7391 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7396 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7398 if ( event->button != 3 )
7401 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7404 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7407 /* Post menu for the currently selected item */
7409 /* See if a track is selected */
7410 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7411 if ( track && track->visible ) {
7413 if ( track->name ) {
7415 if ( vtl->track_right_click_menu )
7416 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
7418 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7425 if ( track->is_route )
7426 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7428 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7430 if ( trkf && udataU.uuid ) {
7433 if ( track->is_route )
7434 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7436 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7438 trw_layer_sublayer_add_menu_items ( vtl,
7439 vtl->track_right_click_menu,
7441 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7447 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7453 /* See if a waypoint is selected */
7454 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7455 if ( waypoint && waypoint->visible ) {
7456 if ( waypoint->name ) {
7458 if ( vtl->wp_right_click_menu )
7459 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7461 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7464 udata.wp = waypoint;
7467 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7469 if ( wpf && udata.uuid ) {
7470 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7472 trw_layer_sublayer_add_menu_items ( vtl,
7473 vtl->wp_right_click_menu,
7475 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7480 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7489 /* background drawing hook, to be passed the viewport */
7490 static gboolean tool_sync_done = TRUE;
7492 static gboolean tool_sync(gpointer data)
7494 VikViewport *vvp = data;
7495 gdk_threads_enter();
7496 vik_viewport_sync(vvp);
7497 tool_sync_done = TRUE;
7498 gdk_threads_leave();
7502 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7505 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7506 gdk_gc_set_function ( t->gc, GDK_INVERT );
7507 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7508 vik_viewport_sync(t->vvp);
7513 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7515 VikViewport *vvp = t->vvp;
7516 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7517 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7521 if (tool_sync_done) {
7522 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7523 tool_sync_done = FALSE;
7527 static void marker_end_move ( tool_ed_t *t )
7529 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7530 g_object_unref ( t->gc );
7534 /*** Edit waypoint ****/
7536 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7538 tool_ed_t *t = g_new(tool_ed_t, 1);
7544 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7546 WPSearchParams params;
7547 tool_ed_t *t = data;
7548 VikViewport *vvp = t->vvp;
7550 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7557 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7560 if ( vtl->current_wp && vtl->current_wp->visible )
7562 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7564 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7566 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7567 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7569 if ( event->button == 3 )
7570 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7572 marker_begin_move(t, event->x, event->y);
7579 params.x = event->x;
7580 params.y = event->y;
7581 params.draw_images = vtl->drawimages;
7582 params.closest_wp_id = NULL;
7583 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7584 params.closest_wp = NULL;
7585 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7586 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7588 // how do we get here?
7589 marker_begin_move(t, event->x, event->y);
7590 g_critical("shouldn't be here");
7593 else if ( params.closest_wp )
7595 if ( event->button == 3 )
7596 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7598 vtl->waypoint_rightclick = FALSE;
7600 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7602 vtl->current_wp = params.closest_wp;
7603 vtl->current_wp_id = params.closest_wp_id;
7605 /* could make it so don't update if old WP is off screen and new is null but oh well */
7606 vik_layer_emit_update ( VIK_LAYER(vtl) );
7610 vtl->current_wp = NULL;
7611 vtl->current_wp_id = NULL;
7612 vtl->waypoint_rightclick = FALSE;
7613 vik_layer_emit_update ( VIK_LAYER(vtl) );
7617 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7619 tool_ed_t *t = data;
7620 VikViewport *vvp = t->vvp;
7622 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7627 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7630 if ( event->state & GDK_CONTROL_MASK )
7632 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7634 new_coord = tp->coord;
7638 if ( event->state & GDK_SHIFT_MASK )
7640 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7641 if ( wp && wp != vtl->current_wp )
7642 new_coord = wp->coord;
7647 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7649 marker_moveto ( t, x, y );
7656 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7658 tool_ed_t *t = data;
7659 VikViewport *vvp = t->vvp;
7661 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7664 if ( t->holding && event->button == 1 )
7667 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7670 if ( event->state & GDK_CONTROL_MASK )
7672 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7674 new_coord = tp->coord;
7678 if ( event->state & GDK_SHIFT_MASK )
7680 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7681 if ( wp && wp != vtl->current_wp )
7682 new_coord = wp->coord;
7685 marker_end_move ( t );
7687 vtl->current_wp->coord = new_coord;
7689 trw_layer_calculate_bounds_waypoints ( vtl );
7690 vik_layer_emit_update ( VIK_LAYER(vtl) );
7693 /* PUT IN RIGHT PLACE!!! */
7694 if ( event->button == 3 && vtl->waypoint_rightclick )
7696 if ( vtl->wp_right_click_menu )
7697 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7698 if ( vtl->current_wp ) {
7699 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7700 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 );
7701 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7703 vtl->waypoint_rightclick = FALSE;
7708 /*** New track ****/
7710 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7717 GdkDrawable *drawable;
7723 * Draw specified pixmap
7725 static gboolean draw_sync ( gpointer data )
7727 draw_sync_t *ds = (draw_sync_t*) data;
7728 // Sometimes don't want to draw
7729 // normally because another update has taken precedent such as panning the display
7730 // which means this pixmap is no longer valid
7731 if ( ds->vtl->draw_sync_do ) {
7732 gdk_threads_enter();
7733 gdk_draw_drawable (ds->drawable,
7736 0, 0, 0, 0, -1, -1);
7737 ds->vtl->draw_sync_done = TRUE;
7738 gdk_threads_leave();
7743 static gchar* distance_string (gdouble distance)
7747 /* draw label with distance */
7748 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7749 switch (dist_units) {
7750 case VIK_UNITS_DISTANCE_MILES:
7751 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7752 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7753 } else if (distance < 1609.4) {
7754 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7756 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7760 // VIK_UNITS_DISTANCE_KILOMETRES
7761 if (distance >= 1000 && distance < 100000) {
7762 g_sprintf(str, "%3.2f km", distance/1000.0);
7763 } else if (distance < 1000) {
7764 g_sprintf(str, "%d m", (int)distance);
7766 g_sprintf(str, "%d km", (int)distance/1000);
7770 return g_strdup (str);
7774 * Actually set the message in statusbar
7776 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
7778 // Only show elevation data when track has some elevation properties
7779 gchar str_gain_loss[64];
7780 str_gain_loss[0] = '\0';
7781 gchar str_last_step[64];
7782 str_last_step[0] = '\0';
7783 gchar *str_total = distance_string (distance);
7785 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7786 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7787 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7789 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7792 if ( last_step > 0 ) {
7793 gchar *tmp = distance_string (last_step);
7794 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
7798 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
7800 // Write with full gain/loss information
7801 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
7802 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
7804 g_free ( str_total );
7808 * Figure out what information should be set in the statusbar and then write it
7810 static void update_statusbar ( VikTrwLayer *vtl )
7812 // Get elevation data
7813 gdouble elev_gain, elev_loss;
7814 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7816 /* Find out actual distance of current track */
7817 gdouble distance = vik_track_get_length (vtl->current_track);
7819 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
7823 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7825 /* if we haven't sync'ed yet, we don't have time to do more. */
7826 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7827 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7828 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7830 static GdkPixmap *pixmap = NULL;
7832 // Need to check in case window has been resized
7833 w1 = vik_viewport_get_width(vvp);
7834 h1 = vik_viewport_get_height(vvp);
7836 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
7838 gdk_drawable_get_size (pixmap, &w2, &h2);
7839 if (w1 != w2 || h1 != h2) {
7840 g_object_unref ( G_OBJECT ( pixmap ) );
7841 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
7844 // Reset to background
7845 gdk_draw_drawable (pixmap,
7846 vtl->current_track_newpoint_gc,
7847 vik_viewport_get_pixmap(vvp),
7848 0, 0, 0, 0, -1, -1);
7850 draw_sync_t *passalong;
7853 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7855 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7856 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7857 // thus when we come to reset to the background it would include what we have already drawn!!
7858 gdk_draw_line ( pixmap,
7859 vtl->current_track_newpoint_gc,
7860 x1, y1, event->x, event->y );
7861 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7863 /* Find out actual distance of current track */
7864 gdouble distance = vik_track_get_length (vtl->current_track);
7866 // Now add distance to where the pointer is //
7869 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7870 vik_coord_to_latlon ( &coord, &ll );
7871 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
7872 distance = distance + last_step;
7874 // Get elevation data
7875 gdouble elev_gain, elev_loss;
7876 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7878 // Adjust elevation data (if available) for the current pointer position
7880 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7881 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7882 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7883 // Adjust elevation of last track point
7884 if ( elev_new > last_tpt->altitude )
7886 elev_gain += elev_new - last_tpt->altitude;
7889 elev_loss += last_tpt->altitude - elev_new;
7894 // Display of the distance 'tooltip' during track creation is controlled by a preference
7896 if ( a_vik_get_create_track_tooltip() ) {
7898 gchar *str = distance_string (distance);
7900 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7901 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
7902 pango_layout_set_text (pl, str, -1);
7904 pango_layout_get_pixel_size ( pl, &wd, &hd );
7907 // offset from cursor a bit depending on font size
7911 // Create a background block to make the text easier to read over the background map
7912 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7913 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7914 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7916 g_object_unref ( G_OBJECT ( pl ) );
7917 g_object_unref ( G_OBJECT ( background_block_gc ) );
7921 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7922 passalong->vtl = vtl;
7923 passalong->pixmap = pixmap;
7924 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
7925 passalong->gc = vtl->current_track_newpoint_gc;
7929 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
7931 // Update statusbar with full gain/loss information
7932 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7934 // draw pixmap when we have time to
7935 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7936 vtl->draw_sync_done = FALSE;
7937 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7939 return VIK_LAYER_TOOL_ACK;
7942 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7944 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7945 vtl->current_track = NULL;
7946 vik_layer_emit_update ( VIK_LAYER(vtl) );
7948 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7950 if ( vtl->current_track->trackpoints )
7952 GList *last = g_list_last(vtl->current_track->trackpoints);
7953 g_free ( last->data );
7954 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7957 update_statusbar ( vtl );
7959 vik_layer_emit_update ( VIK_LAYER(vtl) );
7966 * Common function to handle trackpoint button requests on either a route or a track
7967 * . enables adding a point via normal click
7968 * . enables removal of last point via right click
7969 * . finishing of the track or route via double clicking
7971 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7975 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7978 if ( event->button == 2 ) {
7979 // As the display is panning, the new track pixmap is now invalid so don't draw it
7980 // otherwise this drawing done results in flickering back to an old image
7981 vtl->draw_sync_do = FALSE;
7985 if ( event->button == 3 )
7987 if ( !vtl->current_track )
7990 if ( vtl->current_track->trackpoints )
7992 GList *last = g_list_last(vtl->current_track->trackpoints);
7993 g_free ( last->data );
7994 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7996 vik_track_calculate_bounds ( vtl->current_track );
7997 update_statusbar ( vtl );
7999 vik_layer_emit_update ( VIK_LAYER(vtl) );
8003 if ( event->type == GDK_2BUTTON_PRESS )
8005 /* subtract last (duplicate from double click) tp then end */
8006 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8008 GList *last = g_list_last(vtl->current_track->trackpoints);
8009 g_free ( last->data );
8010 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8011 /* undo last, then end */
8012 vtl->current_track = NULL;
8014 vik_layer_emit_update ( VIK_LAYER(vtl) );
8018 tp = vik_trackpoint_new();
8019 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8021 /* snap to other TP */
8022 if ( event->state & GDK_CONTROL_MASK )
8024 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8026 tp->coord = other_tp->coord;
8029 tp->newsegment = FALSE;
8030 tp->has_timestamp = FALSE;
8033 if ( vtl->current_track ) {
8034 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
8035 /* Auto attempt to get elevation from DEM data (if it's available) */
8036 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8037 vik_track_calculate_bounds ( vtl->current_track );
8040 vtl->ct_x1 = vtl->ct_x2;
8041 vtl->ct_y1 = vtl->ct_y2;
8042 vtl->ct_x2 = event->x;
8043 vtl->ct_y2 = event->y;
8045 vik_layer_emit_update ( VIK_LAYER(vtl) );
8049 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8051 // ----------------------------------------------------- if current is a route - switch to new track
8052 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8054 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8055 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8057 new_track_create_common ( vtl, name );
8062 return tool_new_track_or_route_click ( vtl, event, vvp );
8065 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8067 if ( event->button == 2 ) {
8068 // Pan moving ended - enable potential point drawing again
8069 vtl->draw_sync_do = TRUE;
8070 vtl->draw_sync_done = TRUE;
8074 /*** New route ****/
8076 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8081 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8083 // -------------------------- if current is a track - switch to new route
8084 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8086 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8087 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
8088 new_route_create_common ( vtl, name );
8092 return tool_new_track_or_route_click ( vtl, event, vvp );
8095 /*** New waypoint ****/
8097 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8102 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8105 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8107 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8108 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8109 trw_layer_calculate_bounds_waypoints ( vtl );
8110 vik_layer_emit_update ( VIK_LAYER(vtl) );
8116 /*** Edit trackpoint ****/
8118 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8120 tool_ed_t *t = g_new(tool_ed_t, 1);
8126 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8128 tool_ed_t *t = data;
8129 VikViewport *vvp = t->vvp;
8130 TPSearchParams params;
8131 /* OUTDATED DOCUMENTATION:
8132 find 5 pixel range on each side. then put these UTM, and a pointer
8133 to the winning track name (and maybe the winning track itself), and a
8134 pointer to the winning trackpoint, inside an array or struct. pass
8135 this along, do a foreach on the tracks which will do a foreach on the
8138 params.x = event->x;
8139 params.y = event->y;
8140 params.closest_track_id = NULL;
8141 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8142 params.closest_tp = NULL;
8144 if ( event->button != 1 )
8147 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8150 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8153 if ( vtl->current_tpl )
8155 /* 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.) */
8156 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8157 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8162 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8164 if ( current_tr->visible &&
8165 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8166 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8167 marker_begin_move ( t, event->x, event->y );
8173 if ( vtl->tracks_visible )
8174 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8176 if ( params.closest_tp )
8178 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8179 vtl->current_tpl = params.closest_tpl;
8180 vtl->current_tp_id = params.closest_track_id;
8181 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8182 trw_layer_tpwin_init ( vtl );
8183 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8184 vik_layer_emit_update ( VIK_LAYER(vtl) );
8188 if ( vtl->routes_visible )
8189 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8191 if ( params.closest_tp )
8193 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8194 vtl->current_tpl = params.closest_tpl;
8195 vtl->current_tp_id = params.closest_track_id;
8196 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8197 trw_layer_tpwin_init ( vtl );
8198 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8199 vik_layer_emit_update ( VIK_LAYER(vtl) );
8203 /* these aren't the droids you're looking for */
8207 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8209 tool_ed_t *t = data;
8210 VikViewport *vvp = t->vvp;
8212 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8218 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8221 if ( event->state & GDK_CONTROL_MASK )
8223 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8224 if ( tp && tp != vtl->current_tpl->data )
8225 new_coord = tp->coord;
8227 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8230 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8231 marker_moveto ( t, x, y );
8239 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8241 tool_ed_t *t = data;
8242 VikViewport *vvp = t->vvp;
8244 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8246 if ( event->button != 1)
8251 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8254 if ( event->state & GDK_CONTROL_MASK )
8256 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8257 if ( tp && tp != vtl->current_tpl->data )
8258 new_coord = tp->coord;
8261 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8262 if ( vtl->current_tp_track )
8263 vik_track_calculate_bounds ( vtl->current_tp_track );
8265 marker_end_move ( t );
8267 /* diff dist is diff from orig */
8269 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8271 vik_layer_emit_update ( VIK_LAYER(vtl) );
8278 #ifdef VIK_CONFIG_GOOGLE
8279 /*** Route Finder ***/
8280 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8285 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8288 if ( !vtl ) return FALSE;
8289 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8290 if ( event->button == 3 && vtl->route_finder_current_track ) {
8292 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8294 vtl->route_finder_coord = *new_end;
8296 vik_layer_emit_update ( VIK_LAYER(vtl) );
8297 /* remove last ' to:...' */
8298 if ( vtl->route_finder_current_track->comment ) {
8299 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8300 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8301 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8302 last_to - vtl->route_finder_current_track->comment - 1);
8303 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8308 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8309 struct LatLon start, end;
8310 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
8311 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
8314 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8315 vik_coord_to_latlon ( &(tmp), &end );
8316 vtl->route_finder_coord = tmp; /* for continuations */
8318 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8319 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8320 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8322 vtl->route_finder_check_added_track = TRUE;
8323 vtl->route_finder_started = FALSE;
8326 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
8327 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
8328 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
8329 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
8330 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
8331 // NB normally this returns a GPX Route - so subsequent usage of it must lookup via the routes hash
8332 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL, NULL );
8335 /* see if anything was done -- a track was added or appended to */
8336 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8337 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 ) );
8338 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8339 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8340 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8341 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8344 if ( vtl->route_finder_added_track )
8345 vik_track_calculate_bounds ( vtl->route_finder_added_track );
8347 vtl->route_finder_added_track = NULL;
8348 vtl->route_finder_check_added_track = FALSE;
8349 vtl->route_finder_append = FALSE;
8351 vik_layer_emit_update ( VIK_LAYER(vtl) );
8353 vtl->route_finder_started = TRUE;
8354 vtl->route_finder_coord = tmp;
8355 vtl->route_finder_current_track = NULL;
8361 /*** Show picture ****/
8363 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8368 /* Params are: vvp, event, last match found or NULL */
8369 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8371 if ( wp->image && wp->visible )
8373 gint x, y, slackx, slacky;
8374 GdkEventButton *event = (GdkEventButton *) params[1];
8376 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8377 slackx = wp->image_width / 2;
8378 slacky = wp->image_height / 2;
8379 if ( x <= event->x + slackx && x >= event->x - slackx
8380 && y <= event->y + slacky && y >= event->y - slacky )
8382 params[2] = wp->image; /* we've found a match. however continue searching
8383 * since we want to find the last match -- that
8384 * is, the match that was drawn last. */
8389 static void trw_layer_show_picture ( gpointer pass_along[6] )
8391 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8393 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8396 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8397 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8398 g_free ( quoted_file );
8399 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8401 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() );
8402 g_error_free ( err );
8405 #endif /* WINDOWS */
8408 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8410 gpointer params[3] = { vvp, event, NULL };
8411 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8413 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8416 static gpointer pass_along[6];
8417 pass_along[0] = vtl;
8418 pass_along[5] = params[2];
8419 trw_layer_show_picture ( pass_along );
8420 return TRUE; /* found a match */
8423 return FALSE; /* go through other layers, searching for a match */
8426 /***************************************************************************
8428 ***************************************************************************/
8431 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8433 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8434 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8437 /* Structure for thumbnail creating data used in the background thread */
8439 VikTrwLayer *vtl; // Layer needed for redrawing
8440 GSList *pics; // Image list
8441 } thumbnail_create_thread_data;
8443 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8445 guint total = g_slist_length(tctd->pics), done = 0;
8446 while ( tctd->pics )
8448 a_thumbnails_create ( (gchar *) tctd->pics->data );
8449 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8451 return -1; /* Abort thread */
8453 tctd->pics = tctd->pics->next;
8456 // Redraw to show the thumbnails as they are now created
8457 if ( IS_VIK_LAYER(tctd->vtl) )
8458 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8463 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8465 while ( tctd->pics )
8467 g_free ( tctd->pics->data );
8468 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8473 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8475 if ( ! vtl->has_verified_thumbnails )
8477 GSList *pics = NULL;
8478 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8481 gint len = g_slist_length ( pics );
8482 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8483 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8486 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8488 (vik_thr_func) create_thumbnails_thread,
8490 (vik_thr_free_func) thumbnail_create_thread_free,
8498 static const gchar* my_track_colors ( gint ii )
8500 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8512 // Fast and reliable way of returning a colour
8513 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8516 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8518 GHashTableIter iter;
8519 gpointer key, value;
8523 g_hash_table_iter_init ( &iter, vtl->tracks );
8525 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8527 // Tracks get a random spread of colours if not already assigned
8528 if ( ! VIK_TRACK(value)->has_color ) {
8529 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8530 VIK_TRACK(value)->color = vtl->track_color;
8532 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8534 VIK_TRACK(value)->has_color = TRUE;
8537 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8540 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8546 g_hash_table_iter_init ( &iter, vtl->routes );
8548 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8550 // Routes get an intermix of reds
8551 if ( ! VIK_TRACK(value)->has_color ) {
8553 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8555 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8556 VIK_TRACK(value)->has_color = TRUE;
8559 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8566 * (Re)Calculate the bounds of the waypoints in this layer,
8567 * This should be called whenever waypoints are changed
8569 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
8571 struct LatLon topleft = { 0.0, 0.0 };
8572 struct LatLon bottomright = { 0.0, 0.0 };
8575 GHashTableIter iter;
8576 gpointer key, value;
8578 g_hash_table_iter_init ( &iter, vtl->waypoints );
8580 // Set bounds to first point
8581 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
8582 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
8583 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
8586 // Ensure there is another point...
8587 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
8589 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8591 // See if this point increases the bounds.
8592 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
8594 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
8595 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
8596 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
8597 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
8601 vtl->waypoints_bbox.north = topleft.lat;
8602 vtl->waypoints_bbox.east = bottomright.lon;
8603 vtl->waypoints_bbox.south = bottomright.lat;
8604 vtl->waypoints_bbox.west = topleft.lon;
8607 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
8609 vik_track_calculate_bounds ( trk );
8612 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
8614 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8615 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8618 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
8620 trw_layer_verify_thumbnails ( vtl, vvp );
8621 trw_layer_track_alloc_colors ( vtl );
8623 trw_layer_calculate_bounds_waypoints ( vtl );
8624 trw_layer_calculate_bounds_tracks ( vtl );
8627 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8629 return vtl->coord_mode;
8633 * Uniquify the whole layer
8634 * Also requires the layers panel as the names shown there need updating too
8635 * Returns whether the operation was successful or not
8637 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8640 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8641 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8642 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8648 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8650 vik_coord_convert ( &(wp->coord), *dest_mode );
8653 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
8655 vik_track_convert ( tr, *dest_mode );
8658 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8660 if ( vtl->coord_mode != dest_mode )
8662 vtl->coord_mode = dest_mode;
8663 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8664 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8665 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
8669 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8671 vtl->menu_selection = selection;
8674 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8676 return (vtl->menu_selection);
8679 /* ----------- Downloading maps along tracks --------------- */
8681 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8683 /* TODO: calculating based on current size of viewport */
8684 const gdouble w_at_zoom_0_125 = 0.0013;
8685 const gdouble h_at_zoom_0_125 = 0.0011;
8686 gdouble zoom_factor = zoom_level/0.125;
8688 wh->lat = h_at_zoom_0_125 * zoom_factor;
8689 wh->lon = w_at_zoom_0_125 * zoom_factor;
8691 return 0; /* all OK */
8694 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8696 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8697 (dist->lat >= ABS(to->north_south - from->north_south)))
8700 VikCoord *coord = g_malloc(sizeof(VikCoord));
8701 coord->mode = VIK_COORD_LATLON;
8703 if (ABS(gradient) < 1) {
8704 if (from->east_west > to->east_west)
8705 coord->east_west = from->east_west - dist->lon;
8707 coord->east_west = from->east_west + dist->lon;
8708 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8710 if (from->north_south > to->north_south)
8711 coord->north_south = from->north_south - dist->lat;
8713 coord->north_south = from->north_south + dist->lat;
8714 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8720 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8722 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8723 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8725 VikCoord *next = from;
8727 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8729 list = g_list_prepend(list, next);
8735 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8737 typedef struct _Rect {
8742 #define GLRECT(iter) ((Rect *)((iter)->data))
8745 GList *rects_to_download = NULL;
8748 if (get_download_area_width(vvp, zoom_level, &wh))
8751 GList *iter = tr->trackpoints;
8755 gboolean new_map = TRUE;
8756 VikCoord *cur_coord, tl, br;
8759 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8761 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8762 rect = g_malloc(sizeof(Rect));
8765 rect->center = *cur_coord;
8766 rects_to_download = g_list_prepend(rects_to_download, rect);
8771 gboolean found = FALSE;
8772 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8773 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8784 GList *fillins = NULL;
8785 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8786 /* seems that ATM the function get_next_coord works only for LATLON */
8787 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8788 /* fill-ins for far apart points */
8789 GList *cur_rect, *next_rect;
8790 for (cur_rect = rects_to_download;
8791 (next_rect = cur_rect->next) != NULL;
8792 cur_rect = cur_rect->next) {
8793 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8794 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8795 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8799 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8802 GList *iter = fillins;
8804 cur_coord = (VikCoord *)(iter->data);
8805 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8806 rect = g_malloc(sizeof(Rect));
8809 rect->center = *cur_coord;
8810 rects_to_download = g_list_prepend(rects_to_download, rect);
8815 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8816 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8820 for (iter = fillins; iter; iter = iter->next)
8822 g_list_free(fillins);
8824 if (rects_to_download) {
8825 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8826 g_free(rect_iter->data);
8827 g_list_free(rects_to_download);
8831 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8834 gint selected_map, default_map;
8835 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8836 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8837 gint selected_zoom, default_zoom;
8841 VikTrwLayer *vtl = pass_along[0];
8842 VikLayersPanel *vlp = pass_along[1];
8844 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8845 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8847 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8851 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8853 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8854 int num_maps = g_list_length(vmls);
8857 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8861 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8862 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8864 gchar **np = map_names;
8865 VikMapsLayer **lp = map_layers;
8866 for (i = 0; i < num_maps; i++) {
8867 gboolean dup = FALSE;
8868 vml = (VikMapsLayer *)(vmls->data);
8869 for (j = 0; j < i; j++) { /* no duplicate allowed */
8870 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8877 *np++ = vik_maps_layer_get_map_label(vml);
8883 num_maps = lp - map_layers;
8885 for (default_map = 0; default_map < num_maps; default_map++) {
8886 /* TODO: check for parent layer's visibility */
8887 if (VIK_LAYER(map_layers[default_map])->visible)
8890 default_map = (default_map == num_maps) ? 0 : default_map;
8892 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8893 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8894 if (cur_zoom == zoom_vals[default_zoom])
8897 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8899 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8902 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8905 for (i = 0; i < num_maps; i++)
8906 g_free(map_names[i]);
8914 /**** lowest waypoint number calculation ***/
8915 static gint highest_wp_number_name_to_number(const gchar *name) {
8916 if ( strlen(name) == 3 ) {
8918 if ( n < 100 && name[0] != '0' )
8920 if ( n < 10 && name[0] != '0' )
8928 static void highest_wp_number_reset(VikTrwLayer *vtl)
8930 vtl->highest_wp_number = -1;
8933 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8935 /* if is bigger that top, add it */
8936 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8937 if ( new_wp_num > vtl->highest_wp_number )
8938 vtl->highest_wp_number = new_wp_num;
8941 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8943 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8944 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8945 if ( vtl->highest_wp_number == old_wp_num ) {
8947 vtl->highest_wp_number--;
8949 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8950 /* search down until we find something that *does* exist */
8952 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8953 vtl->highest_wp_number--;
8954 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8959 /* get lowest unused number */
8960 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8963 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8965 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8966 return g_strdup(buf);