2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
9 * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
27 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
34 #include "vikmapslayer.h"
35 #include "vikgpslayer.h"
36 #include "viktrwlayer_tpwin.h"
37 #include "viktrwlayer_propwin.h"
38 #ifdef VIK_CONFIG_GEOTAG
39 #include "viktrwlayer_geotag.h"
40 #include "geotag_exif.h"
42 #include "garminsymbols.h"
43 #include "thumbnails.h"
44 #include "background.h"
49 #include "geonamessearch.h"
50 #ifdef VIK_CONFIG_OPENSTREETMAP
51 #include "osm-traces.h"
54 #include "datasources.h"
55 #include "datasource_gps.h"
56 #include "vikexttool_datasources.h"
59 #include "vikrouting.h"
61 #include "icons/icons.h"
75 #include <gdk/gdkkeysyms.h>
77 #include <glib/gstdio.h>
78 #include <glib/gi18n.h>
80 #define VIK_TRW_LAYER_TRACK_GC 6
81 #define VIK_TRW_LAYER_TRACK_GCS 10
82 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
83 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
84 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
85 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
86 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
87 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
89 #define DRAWMODE_BY_TRACK 0
90 #define DRAWMODE_BY_SPEED 1
91 #define DRAWMODE_ALL_SAME_COLOR 2
92 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
93 // as we are (re)calculating the colour for every point
98 /* this is how it knows when you click if you are clicking close to a trackpoint. */
99 #define TRACKPOINT_SIZE_APPROX 5
100 #define WAYPOINT_SIZE_APPROX 5
102 #define MIN_STOP_LENGTH 15
103 #define MAX_STOP_LENGTH 86400
104 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
105 /* this is multiplied by user-inputted value from 1-100. */
107 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
109 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
111 FS_XX_SMALL = 0, // 'xx-small'
114 FS_MEDIUM, // DEFAULT
121 struct _VikTrwLayer {
124 GHashTable *tracks_iters;
126 GHashTable *routes_iters;
127 GHashTable *waypoints_iters;
128 GHashTable *waypoints;
129 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
130 gboolean tracks_visible, routes_visible, waypoints_visible;
131 LatLonBBox waypoints_bbox;
135 guint8 drawpoints_size;
136 guint8 drawelevation;
137 guint8 elevation_factor;
141 guint8 drawdirections;
142 guint8 drawdirections_size;
143 guint8 line_thickness;
144 guint8 bg_line_thickness;
148 gboolean wp_draw_symbols;
149 font_size_t wp_font_size;
152 gdouble track_draw_speed_factor;
154 GdkGC *track_1color_gc;
155 GdkColor track_color;
156 GdkGC *current_track_gc;
157 // Separate GC for a track's potential new point as drawn via separate method
158 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
159 GdkGC *current_track_newpoint_gc;
160 GdkGC *track_bg_gc; GdkColor track_bg_color;
161 GdkGC *waypoint_gc; GdkColor waypoint_color;
162 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
163 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
165 GdkFont *waypoint_font;
166 VikTrack *current_track; // ATM shared between new tracks and new routes
167 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
168 gboolean draw_sync_done;
169 gboolean draw_sync_do;
171 VikCoordMode coord_mode;
173 /* wp editing tool */
174 VikWaypoint *current_wp;
175 gpointer current_wp_id;
177 gboolean waypoint_rightclick;
179 /* track editing tool */
181 VikTrack *current_tp_track;
182 gpointer current_tp_id;
183 VikTrwLayerTpwin *tpwin;
185 /* track editing tool -- more specifically, moving tps */
188 /* route finder tool */
189 gboolean route_finder_started;
190 VikCoord route_finder_coord;
191 gboolean route_finder_check_added_track;
192 VikTrack *route_finder_added_track;
193 VikTrack *route_finder_current_track;
194 gboolean route_finder_append;
201 guint16 image_cache_size;
203 /* for waypoint text */
204 PangoLayout *wplabellayout;
206 gboolean has_verified_thumbnails;
208 GtkMenu *wp_right_click_menu;
209 GtkMenu *track_right_click_menu;
212 VikStdLayerMenuItem menu_selection;
214 gint highest_wp_number;
217 /* A caached waypoint image. */
220 gchar *image; /* filename */
223 struct DrawingParams {
228 guint16 width, height;
229 gdouble cc; // Cosine factor in track directions
230 gdouble ss; // Sine factor in track directions
231 const VikCoord *center;
232 gboolean one_zone, lat_lon;
233 gdouble ce1, ce2, cn1, cn2;
237 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
239 static void trw_layer_delete_item ( gpointer pass_along[6] );
240 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
241 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
243 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] );
244 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
245 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
247 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
248 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
250 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
251 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
253 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl );
255 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
256 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
257 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
258 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
259 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
260 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
261 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
262 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
263 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
264 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
265 static void trw_layer_append_track ( gpointer pass_along[6] );
266 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
267 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
268 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
269 static void trw_layer_split_segments ( gpointer pass_along[6] );
270 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
271 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
272 static void trw_layer_reverse ( gpointer pass_along[6] );
273 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
274 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
275 static void trw_layer_show_picture ( gpointer pass_along[6] );
276 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
278 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
279 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
280 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
281 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
282 static void trw_layer_new_wp ( gpointer lav[2] );
283 static void trw_layer_new_track ( gpointer lav[2] );
284 static void trw_layer_new_route ( gpointer lav[2] );
285 static void trw_layer_finish_track ( gpointer lav[2] );
286 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
287 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
288 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
289 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
290 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
291 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
292 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
293 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
294 #ifdef VIK_CONFIG_GEOTAG
295 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
296 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
297 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
298 static void trw_layer_geotagging ( gpointer lav[2] );
300 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
301 #ifdef VIK_CONFIG_GOOGLE
302 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
304 #ifdef VIK_CONFIG_OPENSTREETMAP
305 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
306 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
308 #ifdef VIK_CONFIG_GEOCACHES
309 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
311 #ifdef VIK_CONFIG_GEOTAG
312 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
314 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
315 static void trw_layer_gps_upload ( gpointer lav[2] );
317 // Specific route versions:
318 // Most track handling functions can handle operating on the route list
319 // However these ones are easier in separate functions
320 static void trw_layer_auto_routes_view ( gpointer lav[2] );
321 static void trw_layer_delete_all_routes ( gpointer lav[2] );
322 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
325 static void trw_layer_properties_item ( gpointer pass_along[7] );
326 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
327 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
328 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
330 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
331 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
332 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
334 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
335 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
336 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
337 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
339 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
340 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
341 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
342 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
343 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
344 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
345 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
346 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
347 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
348 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
349 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
350 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
351 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
352 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
353 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
354 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
355 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
356 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
357 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
358 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
359 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
361 static void cached_pixbuf_free ( CachedPixbuf *cp );
362 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
364 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
365 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
367 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
368 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
370 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
371 static void highest_wp_number_reset(VikTrwLayer *vtl);
372 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
373 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
375 // Note for the following tool GtkRadioActionEntry texts:
376 // the very first text value is an internal name not displayed anywhere
377 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
378 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
379 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
380 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
381 static VikToolInterface trw_layer_tools[] = {
382 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
383 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
384 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
386 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
388 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
389 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
390 (VikToolMouseFunc) tool_new_track_click,
391 (VikToolMouseMoveFunc) tool_new_track_move,
392 (VikToolMouseFunc) tool_new_track_release,
393 (VikToolKeyFunc) tool_new_track_key_press,
394 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
395 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
397 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
398 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
399 (VikToolMouseFunc) tool_new_route_click,
400 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
401 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
402 (VikToolKeyFunc) tool_new_track_key_press, // -/#
403 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
404 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
406 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
407 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
408 (VikToolMouseFunc) tool_edit_waypoint_click,
409 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
410 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
412 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
414 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
415 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
416 (VikToolMouseFunc) tool_edit_trackpoint_click,
417 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
418 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
420 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
422 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
423 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
424 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
426 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
428 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
429 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
430 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
432 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
436 TOOL_CREATE_WAYPOINT=0,
440 TOOL_EDIT_TRACKPOINT,
446 /****** PARAMETERS ******/
448 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
449 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
451 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
452 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
454 #define MIN_POINT_SIZE 2
455 #define MAX_POINT_SIZE 10
457 #define MIN_ARROW_SIZE 3
458 #define MAX_ARROW_SIZE 20
460 static VikLayerParamScale params_scales[] = {
461 /* min max step digits */
462 { 1, 10, 1, 0 }, /* line_thickness */
463 { 0, 100, 1, 0 }, /* track draw speed factor */
464 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
465 /* 5 * step == how much to turn */
466 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
467 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
468 { 5, 500, 5, 0 }, // 5: image cache_size - " "
469 { 0, 8, 1, 0 }, // 6: Background line thickness
470 { 1, 64, 1, 0 }, /* wpsize */
471 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
472 { 1, 100, 1, 0 }, // 9: elevation factor
473 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
474 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
477 static gchar* params_font_sizes[] = {
478 N_("Extra Extra Small"),
484 N_("Extra Extra Large"),
487 static VikLayerParamData black_color_default ( void ) {
488 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
490 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
491 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
492 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
493 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
494 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
495 static VikLayerParamData trackbgcolor_default ( void ) {
496 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
498 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
499 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
500 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
502 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
503 static VikLayerParamData wptextcolor_default ( void ) {
504 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
506 static VikLayerParamData wpbgcolor_default ( void ) {
507 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
509 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
510 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
512 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
513 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
514 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
516 VikLayerParam trw_layer_params[] = {
517 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
518 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
519 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
521 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default },
522 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
523 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default },
524 { 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 },
525 { 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 },
526 { 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 },
527 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL, trkdirectionsize_default },
528 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default },
529 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL, trkpointsize_default },
530 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default },
531 { 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 },
533 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
534 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 },
535 { 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 },
537 { 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 },
538 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default },
539 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
540 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default },
542 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default },
543 { 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 },
544 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default },
545 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default },
546 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default },
547 { 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 },
548 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default },
549 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[7], NULL, NULL, wpsize_default },
550 { 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 },
552 { 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 },
553 { 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 },
554 { 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 },
555 { 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 },
558 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
560 // Sublayer visibilities
599 *** 1) Add to trw_layer_params and enumeration
600 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
603 /****** END PARAMETERS ******/
605 /* Layer Interface function definitions */
606 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
607 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
608 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
609 static void trw_layer_free ( VikTrwLayer *trwlayer );
610 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
611 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
612 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
613 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
614 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
615 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
616 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
617 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
618 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
619 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
620 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
621 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
622 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
623 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
624 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
625 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
626 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
627 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
628 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
629 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
630 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
631 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
632 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
633 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
634 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
635 /* End Layer Interface function definitions */
637 VikLayerInterface vik_trw_layer_interface = {
644 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
648 params_groups, /* params_groups */
649 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
653 (VikLayerFuncCreate) trw_layer_create,
654 (VikLayerFuncRealize) trw_layer_realize,
655 (VikLayerFuncPostRead) trw_layer_post_read,
656 (VikLayerFuncFree) trw_layer_free,
658 (VikLayerFuncProperties) NULL,
659 (VikLayerFuncDraw) trw_layer_draw,
660 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
662 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
663 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
665 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
666 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
668 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
669 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
670 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
671 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
672 (VikLayerFuncLayerSelected) trw_layer_selected,
674 (VikLayerFuncMarshall) trw_layer_marshall,
675 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
677 (VikLayerFuncSetParam) trw_layer_set_param,
678 (VikLayerFuncGetParam) trw_layer_get_param,
680 (VikLayerFuncReadFileData) a_gpspoint_read_file,
681 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
683 (VikLayerFuncDeleteItem) trw_layer_del_item,
684 (VikLayerFuncCutItem) trw_layer_cut_item,
685 (VikLayerFuncCopyItem) trw_layer_copy_item,
686 (VikLayerFuncPasteItem) trw_layer_paste_item,
687 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
689 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
691 (VikLayerFuncSelectClick) trw_layer_select_click,
692 (VikLayerFuncSelectMove) trw_layer_select_move,
693 (VikLayerFuncSelectRelease) trw_layer_select_release,
694 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
697 GType vik_trw_layer_get_type ()
699 static GType vtl_type = 0;
703 static const GTypeInfo vtl_info =
705 sizeof (VikTrwLayerClass),
706 NULL, /* base_init */
707 NULL, /* base_finalize */
708 NULL, /* class init */
709 NULL, /* class_finalize */
710 NULL, /* class_data */
711 sizeof (VikTrwLayer),
713 NULL /* instance init */
715 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
721 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
723 static gpointer pass_along[6];
729 pass_along[1] = NULL;
730 pass_along[2] = GINT_TO_POINTER (subtype);
731 pass_along[3] = sublayer;
732 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
733 pass_along[5] = NULL;
735 trw_layer_delete_item ( pass_along );
738 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
740 static gpointer pass_along[6];
746 pass_along[1] = NULL;
747 pass_along[2] = GINT_TO_POINTER (subtype);
748 pass_along[3] = sublayer;
749 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
750 pass_along[5] = NULL;
752 trw_layer_copy_item_cb(pass_along);
753 trw_layer_cut_item_cb(pass_along);
756 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
758 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
759 gint subtype = GPOINTER_TO_INT (pass_along[2]);
760 gpointer * sublayer = pass_along[3];
764 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
768 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
769 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
770 if ( wp && wp->name )
773 name = NULL; // Broken :(
775 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
776 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
777 if ( trk && trk->name )
780 name = NULL; // Broken :(
783 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
784 if ( trk && trk->name )
787 name = NULL; // Broken :(
790 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
791 subtype, len, name, data);
795 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
797 trw_layer_copy_item_cb(pass_along);
798 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
799 trw_layer_delete_item(pass_along);
802 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
804 // Slightly cheating method, routing via the panels capability
805 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
808 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
818 GByteArray *ba = g_byte_array_new ();
820 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
821 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
822 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
823 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
825 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
828 g_byte_array_append ( ba, id, il );
836 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
843 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
847 w = vik_waypoint_unmarshall ( item, len );
848 // When copying - we'll create a new name based on the original
849 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
850 vik_trw_layer_add_waypoint ( vtl, name, w );
851 waypoint_convert (NULL, w, &vtl->coord_mode);
853 trw_layer_calculate_bounds_waypoints ( vtl );
855 // Consider if redraw necessary for the new item
856 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
857 vik_layer_emit_update ( VIK_LAYER(vtl) );
860 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
864 t = vik_track_unmarshall ( item, len );
865 // When copying - we'll create a new name based on the original
866 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
867 vik_trw_layer_add_track ( vtl, name, t );
868 vik_track_convert (t, vtl->coord_mode);
870 // Consider if redraw necessary for the new item
871 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
872 vik_layer_emit_update ( VIK_LAYER(vtl) );
875 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
879 t = vik_track_unmarshall ( item, len );
880 // When copying - we'll create a new name based on the original
881 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
882 vik_trw_layer_add_route ( vtl, name, t );
883 vik_track_convert (t, vtl->coord_mode);
885 // Consider if redraw necessary for the new item
886 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
887 vik_layer_emit_update ( VIK_LAYER(vtl) );
893 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
900 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
904 case PARAM_TV: vtl->tracks_visible = data.b; break;
905 case PARAM_WV: vtl->waypoints_visible = data.b; break;
906 case PARAM_RV: vtl->routes_visible = data.b; break;
907 case PARAM_DM: vtl->drawmode = data.u; break;
909 vtl->track_color = data.c;
910 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
912 case PARAM_DP: vtl->drawpoints = data.b; break;
914 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
915 vtl->drawpoints_size = data.u;
917 case PARAM_DE: vtl->drawelevation = data.b; break;
918 case PARAM_DS: vtl->drawstops = data.b; break;
919 case PARAM_DL: vtl->drawlines = data.b; break;
920 case PARAM_DD: vtl->drawdirections = data.b; break;
922 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
923 vtl->drawdirections_size = data.u;
925 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
926 vtl->stop_length = data.u;
928 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
929 vtl->elevation_factor = data.u;
931 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
933 vtl->line_thickness = data.u;
934 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
937 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
939 vtl->bg_line_thickness = data.u;
940 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
944 vtl->track_bg_color = data.c;
945 if ( vtl->track_bg_gc )
946 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
948 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
949 case PARAM_DLA: vtl->drawlabels = data.b; break;
950 case PARAM_DI: vtl->drawimages = data.b; break;
951 case PARAM_IS: if ( data.u != vtl->image_size )
953 vtl->image_size = data.u;
954 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
955 g_queue_free ( vtl->image_cache );
956 vtl->image_cache = g_queue_new ();
959 case PARAM_IA: vtl->image_alpha = data.u; break;
960 case PARAM_ICS: vtl->image_cache_size = data.u;
961 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
962 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
965 vtl->waypoint_color = data.c;
966 if ( vtl->waypoint_gc )
967 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
970 vtl->waypoint_text_color = data.c;
971 if ( vtl->waypoint_text_gc )
972 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
975 vtl->waypoint_bg_color = data.c;
976 if ( vtl->waypoint_bg_gc )
977 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
980 vtl->wpbgand = data.b;
981 if ( vtl->waypoint_bg_gc )
982 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
984 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
985 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
986 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
987 case PARAM_WPFONTSIZE:
988 if ( data.u < FS_NUM_SIZES ) {
989 vtl->wp_font_size = data.u;
990 g_free ( vtl->wp_fsize_str );
991 switch ( vtl->wp_font_size ) {
992 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
993 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
994 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
995 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
996 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
997 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
998 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1006 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1008 VikLayerParamData rv;
1011 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1012 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1013 case PARAM_RV: rv.b = vtl->routes_visible; break;
1014 case PARAM_DM: rv.u = vtl->drawmode; break;
1015 case PARAM_TC: rv.c = vtl->track_color; break;
1016 case PARAM_DP: rv.b = vtl->drawpoints; break;
1017 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1018 case PARAM_DE: rv.b = vtl->drawelevation; break;
1019 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1020 case PARAM_DS: rv.b = vtl->drawstops; break;
1021 case PARAM_SL: rv.u = vtl->stop_length; break;
1022 case PARAM_DL: rv.b = vtl->drawlines; break;
1023 case PARAM_DD: rv.b = vtl->drawdirections; break;
1024 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1025 case PARAM_LT: rv.u = vtl->line_thickness; break;
1026 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1027 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1028 case PARAM_DI: rv.b = vtl->drawimages; break;
1029 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1030 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1031 case PARAM_IS: rv.u = vtl->image_size; break;
1032 case PARAM_IA: rv.u = vtl->image_alpha; break;
1033 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1034 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1035 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1036 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1037 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1038 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1039 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1040 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1041 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1046 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1053 // Use byte arrays to store sublayer data
1054 // much like done elsewhere e.g. vik_layer_marshall_params()
1055 GByteArray *ba = g_byte_array_new ( );
1060 guint object_length;
1063 // the length of the item
1064 // the sublayer type of item
1065 // the the actual item
1066 #define tlm_append(object_pointer, size, type) \
1068 object_length = (size); \
1069 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1070 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1071 g_byte_array_append ( ba, (object_pointer), object_length );
1073 // Layer parameters first
1074 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1075 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1076 g_byte_array_append ( ba, pd, pl );
1079 // Now sublayer data
1080 GHashTableIter iter;
1081 gpointer key, value;
1084 g_hash_table_iter_init ( &iter, vtl->waypoints );
1085 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1086 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1087 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1092 g_hash_table_iter_init ( &iter, vtl->tracks );
1093 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1094 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1095 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1100 g_hash_table_iter_init ( &iter, vtl->routes );
1101 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1102 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1103 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1113 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1115 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1117 gint consumed_length;
1119 // First the overall layer parameters
1120 memcpy(&pl, data, sizeof(pl));
1122 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1125 consumed_length = pl;
1126 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1128 #define tlm_size (*(gint *)data)
1129 // See marshalling above for order of how this is written
1131 data += sizeof_len_and_subtype + tlm_size;
1133 // Now the individual sublayers:
1135 while ( *data && consumed_length < len ) {
1136 // Normally four extra bytes at the end of the datastream
1137 // (since it's a GByteArray and that's where it's length is stored)
1138 // So only attempt read when there's an actual block of sublayer data
1139 if ( consumed_length + tlm_size < len ) {
1141 // Reuse pl to read the subtype from the data stream
1142 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1144 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1145 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1146 gchar *name = g_strdup ( trk->name );
1147 vik_trw_layer_add_track ( vtl, name, trk );
1150 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1151 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1152 gchar *name = g_strdup ( wp->name );
1153 vik_trw_layer_add_waypoint ( vtl, name, wp );
1156 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1157 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1158 gchar *name = g_strdup ( trk->name );
1159 vik_trw_layer_add_route ( vtl, name, trk );
1163 consumed_length += tlm_size + sizeof_len_and_subtype;
1166 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1168 // Not stored anywhere else so need to regenerate
1169 trw_layer_calculate_bounds_waypoints ( vtl );
1174 // Keep interesting hash function at least visible
1176 static guint strcase_hash(gconstpointer v)
1178 // 31 bit hash function
1181 gchar s[128]; // malloc is too slow for reading big files
1184 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1185 p[i] = toupper(t[i]);
1191 for (p += 1; *p != '\0'; p++)
1192 h = (h << 5) - h + *p;
1199 // Stick a 1 at the end of the function name to make it more unique
1200 // thus more easily searchable in a simple text editor
1201 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1203 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1204 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1206 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1207 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1209 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1210 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1211 // and with normal PC processing capabilities - it has negligibile performance impact
1212 // This also minimized the amount of rework - as the management of the hash tables already exists.
1214 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1215 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1216 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1218 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1219 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1220 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1221 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1222 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1223 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1225 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1227 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1229 // Param settings that are not available via the GUI
1230 // Force to on after processing params (which defaults them to off with a zero value)
1231 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1233 rv->draw_sync_done = TRUE;
1234 rv->draw_sync_do = TRUE;
1240 static void trw_layer_free ( VikTrwLayer *trwlayer )
1242 g_hash_table_destroy(trwlayer->waypoints);
1243 g_hash_table_destroy(trwlayer->tracks);
1245 /* ODC: replace with GArray */
1246 trw_layer_free_track_gcs ( trwlayer );
1248 if ( trwlayer->wp_right_click_menu )
1249 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1251 if ( trwlayer->track_right_click_menu )
1252 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1254 if ( trwlayer->wplabellayout != NULL)
1255 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1257 if ( trwlayer->waypoint_gc != NULL )
1258 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1260 if ( trwlayer->waypoint_text_gc != NULL )
1261 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1263 if ( trwlayer->waypoint_bg_gc != NULL )
1264 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1266 g_free ( trwlayer->wp_fsize_str );
1268 if ( trwlayer->tpwin != NULL )
1269 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1271 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1272 g_queue_free ( trwlayer->image_cache );
1275 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1279 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1280 dp->xmpp = vik_viewport_get_xmpp ( vp );
1281 dp->ympp = vik_viewport_get_ympp ( vp );
1282 dp->width = vik_viewport_get_width ( vp );
1283 dp->height = vik_viewport_get_height ( vp );
1284 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1285 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1287 dp->center = vik_viewport_get_center ( vp );
1288 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1289 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1294 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1295 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1296 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1298 dp->ce1 = dp->center->east_west-w2;
1299 dp->ce2 = dp->center->east_west+w2;
1300 dp->cn1 = dp->center->north_south-h2;
1301 dp->cn2 = dp->center->north_south+h2;
1302 } else if ( dp->lat_lon ) {
1303 VikCoord upperleft, bottomright;
1304 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1305 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1306 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1307 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1308 dp->ce1 = upperleft.east_west;
1309 dp->ce2 = bottomright.east_west;
1310 dp->cn1 = bottomright.north_south;
1311 dp->cn2 = upperleft.north_south;
1314 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1318 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1319 * Here a simple traffic like light colour system is used:
1320 * . slow points are red
1321 * . average is yellow
1322 * . fast points are green
1324 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1327 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1328 if ( average_speed > 0 ) {
1329 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1330 if ( rv < low_speed )
1331 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1332 else if ( rv > high_speed )
1333 return VIK_TRW_LAYER_TRACK_GC_FAST;
1335 return VIK_TRW_LAYER_TRACK_GC_AVER;
1338 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1341 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1343 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1344 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1345 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1346 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1349 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1351 if ( ! track->visible )
1354 /* TODO: this function is a mess, get rid of any redundancy */
1355 GList *list = track->trackpoints;
1357 gboolean useoldvals = TRUE;
1359 gboolean drawpoints;
1361 gboolean drawelevation;
1362 gdouble min_alt, max_alt, alt_diff = 0;
1364 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1365 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1368 if ( dp->vtl->drawelevation )
1370 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1371 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1372 alt_diff = max_alt - min_alt;
1375 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1376 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1377 trw_layer_draw_track ( id, track, dp, TRUE );
1379 if ( draw_track_outline )
1380 drawpoints = drawstops = FALSE;
1382 drawpoints = dp->vtl->drawpoints;
1383 drawstops = dp->vtl->drawstops;
1386 gboolean drawing_highlight = FALSE;
1387 /* Current track - used for creation */
1388 if ( track == dp->vtl->current_track )
1389 main_gc = dp->vtl->current_track_gc;
1391 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1392 /* Draw all tracks of the layer in special colour */
1393 /* if track is member of selected layer or is the current selected track
1394 then draw in the highlight colour.
1395 NB this supercedes the drawmode */
1396 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1397 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1398 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1399 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1400 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1401 drawing_highlight = TRUE;
1404 if ( !drawing_highlight ) {
1405 // Still need to figure out the gc according to the drawing mode:
1406 switch ( dp->vtl->drawmode ) {
1407 case DRAWMODE_BY_TRACK:
1408 if ( dp->vtl->track_1color_gc )
1409 g_object_unref ( dp->vtl->track_1color_gc );
1410 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1411 main_gc = dp->vtl->track_1color_gc;
1414 // Mostly for DRAWMODE_ALL_SAME_COLOR
1415 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1416 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1423 int x, y, oldx, oldy;
1424 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1426 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1428 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1430 // Draw the first point as something a bit different from the normal points
1431 // ATM it's slightly bigger and a triangle
1433 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1434 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1440 gdouble average_speed = 0.0;
1441 gdouble low_speed = 0.0;
1442 gdouble high_speed = 0.0;
1443 // If necessary calculate these values - which is done only once per track redraw
1444 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1445 // the percentage factor away from the average speed determines transistions between the levels
1446 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1447 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1448 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1451 while ((list = g_list_next(list)))
1453 tp = VIK_TRACKPOINT(list->data);
1454 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1456 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1457 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1458 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1459 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1460 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1462 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1465 * If points are the same in display coordinates, don't draw.
1467 if ( useoldvals && x == oldx && y == oldy )
1469 // Still need to process points to ensure 'stops' are drawn if required
1470 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1471 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1472 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 );
1477 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1478 if ( drawpoints || dp->vtl->drawlines ) {
1479 // setup main_gc for both point and line drawing
1480 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1481 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 ) );
1485 if ( drawpoints && ! draw_track_outline )
1490 * The concept of drawing stops is that a trackpoint
1491 * that is if the next trackpoint has a timestamp far into
1492 * the future, we draw a circle of 6x trackpoint size,
1493 * instead of a rectangle of 2x trackpoint size.
1494 * This is drawn first so the trackpoint will be drawn on top
1497 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1498 /* Stop point. Draw 6x circle. Always in redish colour */
1499 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 );
1501 /* Regular point - draw 2x square. */
1502 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1505 /* Final point - draw 4x circle. */
1506 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 );
1509 if ((!tp->newsegment) && (dp->vtl->drawlines))
1512 /* UTM only: zone check */
1513 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1514 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1517 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1519 if ( draw_track_outline ) {
1520 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1524 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1526 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1528 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1533 tmp[1].y = oldy-FIXALTITUDE(list->data);
1535 tmp[2].y = y-FIXALTITUDE(list->next->data);
1540 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1541 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1543 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1544 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1546 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1551 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1552 // Draw an arrow at the mid point to show the direction of the track
1553 // Code is a rework from vikwindow::draw_ruler()
1554 gint midx = (oldx + x) / 2;
1555 gint midy = (oldy + y) / 2;
1557 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1558 // Avoid divide by zero and ensure at least 1 pixel big
1560 gdouble dx = (oldx - midx) / len;
1561 gdouble dy = (oldy - midy) / len;
1562 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1563 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1573 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1575 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1576 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1578 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1580 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1581 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 ));
1585 * If points are the same in display coordinates, don't draw.
1587 if ( x != oldx || y != oldy )
1589 if ( draw_track_outline )
1590 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1592 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1598 * If points are the same in display coordinates, don't draw.
1600 if ( x != oldx && y != oldy )
1602 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1603 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1613 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1615 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1616 trw_layer_draw_track ( id, track, dp, FALSE );
1620 static void cached_pixbuf_free ( CachedPixbuf *cp )
1622 g_object_unref ( G_OBJECT(cp->pixbuf) );
1623 g_free ( cp->image );
1626 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1628 return strcmp ( cp->image, name );
1631 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1634 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1635 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1636 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1639 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1641 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1643 if ( wp->image && dp->vtl->drawimages )
1645 GdkPixbuf *pixbuf = NULL;
1648 if ( dp->vtl->image_alpha == 0)
1651 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1653 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1656 gchar *image = wp->image;
1657 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1658 if ( ! regularthumb )
1660 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1661 image = "\x12\x00"; /* this shouldn't occur naturally. */
1665 CachedPixbuf *cp = NULL;
1666 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1667 if ( dp->vtl->image_size == 128 )
1668 cp->pixbuf = regularthumb;
1671 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1672 g_assert ( cp->pixbuf );
1673 g_object_unref ( G_OBJECT(regularthumb) );
1675 cp->image = g_strdup ( image );
1677 /* needed so 'click picture' tool knows how big the pic is; we don't
1678 * store it in cp because they may have been freed already. */
1679 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1680 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1682 g_queue_push_head ( dp->vtl->image_cache, cp );
1683 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1684 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1686 pixbuf = cp->pixbuf;
1690 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1696 w = gdk_pixbuf_get_width ( pixbuf );
1697 h = gdk_pixbuf_get_height ( pixbuf );
1699 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1701 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1702 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1703 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1704 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
1705 // Highlighted - so draw a little border around the chosen one
1706 // single line seems a little weak so draw 2 of them
1707 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1708 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1709 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1710 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1713 if ( dp->vtl->image_alpha == 255 )
1714 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1716 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1718 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1722 // Draw appropriate symbol - either symbol image or simple types
1723 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
1724 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 );
1726 else if ( wp == dp->vtl->current_wp ) {
1727 switch ( dp->vtl->wp_symbol ) {
1728 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;
1729 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;
1730 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;
1731 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 );
1732 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 );
1736 switch ( dp->vtl->wp_symbol ) {
1737 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;
1738 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;
1739 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;
1740 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 );
1741 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;
1745 if ( dp->vtl->drawlabels )
1747 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1748 gint label_x, label_y;
1750 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1752 // Could this stored in the waypoint rather than recreating each pass?
1753 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
1755 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1756 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1758 // Fallback if parse failure
1759 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1761 g_free ( wp_label_markup );
1763 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1764 label_x = x - width/2;
1765 if ( wp->symbol_pixbuf )
1766 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
1768 label_y = y - dp->vtl->wp_size - height - 2;
1770 /* if highlight mode on, then draw background text in highlight colour */
1771 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1772 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1773 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1774 wp == vik_window_get_selected_waypoint ( dp->vw ) )
1775 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1777 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1780 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1782 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1787 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1789 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
1790 trw_layer_draw_waypoint ( id, wp, dp );
1794 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1796 static struct DrawingParams dp;
1797 g_assert ( l != NULL );
1799 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1801 if ( l->tracks_visible )
1802 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1804 if ( l->routes_visible )
1805 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1807 if (l->waypoints_visible)
1808 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
1811 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1814 if ( vtl->track_bg_gc )
1816 g_object_unref ( vtl->track_bg_gc );
1817 vtl->track_bg_gc = NULL;
1819 if ( vtl->track_1color_gc )
1821 g_object_unref ( vtl->track_1color_gc );
1822 vtl->track_1color_gc = NULL;
1824 if ( vtl->current_track_gc )
1826 g_object_unref ( vtl->current_track_gc );
1827 vtl->current_track_gc = NULL;
1829 if ( vtl->current_track_newpoint_gc )
1831 g_object_unref ( vtl->current_track_newpoint_gc );
1832 vtl->current_track_newpoint_gc = NULL;
1835 if ( ! vtl->track_gc )
1837 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1838 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1839 g_array_free ( vtl->track_gc, TRUE );
1840 vtl->track_gc = NULL;
1843 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1845 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1846 gint width = vtl->line_thickness;
1848 if ( vtl->track_gc )
1849 trw_layer_free_track_gcs ( vtl );
1851 if ( vtl->track_bg_gc )
1852 g_object_unref ( vtl->track_bg_gc );
1853 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
1855 // Ensure new track drawing heeds line thickness setting
1856 // however always have a minium of 2, as 1 pixel is really narrow
1857 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1859 if ( vtl->current_track_gc )
1860 g_object_unref ( vtl->current_track_gc );
1861 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1862 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1864 // 'newpoint' gc is exactly the same as the current track gc
1865 if ( vtl->current_track_newpoint_gc )
1866 g_object_unref ( vtl->current_track_newpoint_gc );
1867 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1868 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1870 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1872 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1873 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1875 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1876 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1877 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1879 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
1881 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1884 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1886 VikTrwLayer *rv = trw_layer_new1 ( vp );
1887 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1889 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
1890 /* early exit, as the rest is GUI related */
1894 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1895 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
1897 trw_layer_new_track_gcs ( rv, vp );
1899 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
1900 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
1901 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
1902 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
1904 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1906 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1911 #define SMALL_ICON_SIZE 18
1913 * Can accept a null symbol, and may return null value
1915 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1917 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1918 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1919 // So needing a small icon for the treeview may need some resizing:
1920 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1921 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1925 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1927 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1929 GdkPixbuf *pixbuf = NULL;
1931 if ( track->has_color ) {
1932 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1933 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1934 // Here is some magic found to do the conversion
1935 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1936 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1937 ((track->color.green & 0xff00) << 8) |
1938 (track->color.blue & 0xff00);
1940 gdk_pixbuf_fill ( pixbuf, pixel );
1943 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1944 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 );
1946 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 );
1950 g_object_unref (pixbuf);
1952 *new_iter = *((GtkTreeIter *) pass_along[1]);
1953 if ( track->is_route )
1954 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1956 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1958 if ( ! track->visible )
1959 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1962 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1964 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1966 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1967 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 );
1969 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 );
1972 *new_iter = *((GtkTreeIter *) pass_along[1]);
1973 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1975 if ( ! wp->visible )
1976 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1979 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1981 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1982 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1984 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1988 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1990 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1991 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1993 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1997 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1999 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2000 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2002 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2006 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2009 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2011 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2012 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2013 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2015 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
2018 if ( g_hash_table_size (vtl->routes) > 0 ) {
2020 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2022 pass_along[0] = &(vtl->routes_iter);
2023 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2025 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2027 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2030 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2031 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2033 pass_along[0] = &(vtl->waypoints_iter);
2034 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2036 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2038 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2043 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2047 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2048 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2049 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2050 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2052 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2054 return (t->visible ^= 1);
2058 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2060 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2062 return (t->visible ^= 1);
2066 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2068 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2070 return (t->visible ^= 1);
2079 * Return a property about tracks for this layer
2081 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2083 return vtl->line_thickness;
2086 // Structure to hold multiple track information for a layer
2095 * Build up layer multiple track information via updating the tooltip_tracks structure
2097 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2099 tt->length = tt->length + vik_track_get_length (tr);
2101 // Ensure times are available
2102 if ( tr->trackpoints &&
2103 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2104 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2107 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2108 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2110 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2111 // Hence initialize to the first 'proper' value
2112 if ( tt->start_time == 0 )
2113 tt->start_time = t1;
2114 if ( tt->end_time == 0 )
2117 // Update find the earliest / last times
2118 if ( t1 < tt->start_time )
2119 tt->start_time = t1;
2120 if ( t2 > tt->end_time )
2123 // Keep track of total time
2124 // there maybe gaps within a track (eg segments)
2125 // but this should be generally good enough for a simple indicator
2126 tt->duration = tt->duration + (int)(t2-t1);
2131 * Generate tooltip text for the layer.
2132 * This is relatively complicated as it considers information for
2133 * no tracks, a single track or multiple tracks
2134 * (which may or may not have timing information)
2136 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2147 static gchar tmp_buf[128];
2150 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2152 // Safety check - I think these should always be valid
2153 if ( vtl->tracks && vtl->waypoints ) {
2154 tooltip_tracks tt = { 0.0, 0, 0 };
2155 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2157 GDate* gdate_start = g_date_new ();
2158 g_date_set_time_t (gdate_start, tt.start_time);
2160 GDate* gdate_end = g_date_new ();
2161 g_date_set_time_t (gdate_end, tt.end_time);
2163 if ( g_date_compare (gdate_start, gdate_end) ) {
2164 // Dates differ so print range on separate line
2165 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2166 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2167 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2170 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2171 if ( tt.start_time != 0 )
2172 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2176 if ( tt.length > 0.0 ) {
2177 gdouble len_in_units;
2179 // Setup info dependent on distance units
2180 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2181 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2182 len_in_units = VIK_METERS_TO_MILES(tt.length);
2185 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2186 len_in_units = tt.length/1000.0;
2189 // Timing information if available
2191 if ( tt.duration > 0 ) {
2192 g_snprintf (tbuf1, sizeof(tbuf1),
2193 _(" in %d:%02d hrs:mins"),
2194 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2196 g_snprintf (tbuf2, sizeof(tbuf2),
2197 _("\n%sTotal Length %.1f %s%s"),
2198 tbuf3, len_in_units, tbuf4, tbuf1);
2201 // Put together all the elements to form compact tooltip text
2202 g_snprintf (tmp_buf, sizeof(tmp_buf),
2203 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2204 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2206 g_date_free (gdate_start);
2207 g_date_free (gdate_end);
2214 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2218 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2220 // Very simple tooltip - may expand detail in the future...
2221 static gchar tmp_buf[32];
2222 g_snprintf (tmp_buf, sizeof(tmp_buf),
2224 g_hash_table_size (l->tracks));
2228 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2230 // Very simple tooltip - may expand detail in the future...
2231 static gchar tmp_buf[32];
2232 g_snprintf (tmp_buf, sizeof(tmp_buf),
2234 g_hash_table_size (l->routes));
2239 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2240 // Same tooltip for a route
2241 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2244 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2245 tr = g_hash_table_lookup ( l->tracks, sublayer );
2247 tr = g_hash_table_lookup ( l->routes, sublayer );
2250 // Could be a better way of handling strings - but this works...
2251 gchar time_buf1[20];
2252 gchar time_buf2[20];
2253 time_buf1[0] = '\0';
2254 time_buf2[0] = '\0';
2255 static gchar tmp_buf[100];
2256 // Compact info: Short date eg (11/20/99), duration and length
2257 // Hopefully these are the things that are most useful and so promoted into the tooltip
2258 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2259 // %x The preferred date representation for the current locale without the time.
2260 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2261 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2262 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2264 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2267 // Get length and consider the appropriate distance units
2268 gdouble tr_len = vik_track_get_length(tr);
2269 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2270 switch (dist_units) {
2271 case VIK_UNITS_DISTANCE_KILOMETRES:
2272 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2274 case VIK_UNITS_DISTANCE_MILES:
2275 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2284 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2286 // Very simple tooltip - may expand detail in the future...
2287 static gchar tmp_buf[32];
2288 g_snprintf (tmp_buf, sizeof(tmp_buf),
2290 g_hash_table_size (l->waypoints));
2294 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2296 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2297 // NB It's OK to return NULL
2302 return w->description;
2312 * Function to show basic track point information on the statusbar
2314 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2317 switch (a_vik_get_units_height ()) {
2318 case VIK_UNITS_HEIGHT_FEET:
2319 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2322 //VIK_UNITS_HEIGHT_METRES:
2323 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2328 if ( trkpt->has_timestamp ) {
2329 // Compact date time format
2330 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2334 // Position is put later on, as this bit may not be seen if the display is not big enough,
2335 // one can easily use the current pointer position to see this if needed
2336 gchar *lat = NULL, *lon = NULL;
2337 static struct LatLon ll;
2338 vik_coord_to_latlon (&(trkpt->coord), &ll);
2339 a_coords_latlon_to_string ( &ll, &lat, &lon );
2342 // Again is put later on, as this bit may not be seen if the display is not big enough
2343 // trackname can be seen from the treeview (when enabled)
2344 // Also name could be very long to not leave room for anything else
2347 if ( vtl->current_tp_track ) {
2348 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2351 // Combine parts to make overall message
2352 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2353 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2360 * Function to show basic waypoint information on the statusbar
2362 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2365 switch (a_vik_get_units_height ()) {
2366 case VIK_UNITS_HEIGHT_FEET:
2367 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2370 //VIK_UNITS_HEIGHT_METRES:
2371 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2375 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2376 // one can easily use the current pointer position to see this if needed
2377 gchar *lat = NULL, *lon = NULL;
2378 static struct LatLon ll;
2379 vik_coord_to_latlon (&(wpt->coord), &ll);
2380 a_coords_latlon_to_string ( &ll, &lat, &lon );
2382 // Combine parts to make overall message
2385 // Add comment if available
2386 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2388 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2389 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2396 * General layer selection function, find out which bit is selected and take appropriate action
2398 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2401 l->current_wp = NULL;
2402 l->current_wp_id = NULL;
2403 trw_layer_cancel_current_tp ( l, FALSE );
2406 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2410 case VIK_TREEVIEW_TYPE_LAYER:
2412 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2413 /* Mark for redraw */
2418 case VIK_TREEVIEW_TYPE_SUBLAYER:
2422 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2424 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2425 /* Mark for redraw */
2429 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2431 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2432 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2433 /* Mark for redraw */
2437 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2439 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2440 /* Mark for redraw */
2444 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2446 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2447 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2448 /* Mark for redraw */
2452 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2454 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2455 /* Mark for redraw */
2459 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2461 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2463 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2464 // Show some waypoint info
2465 set_statusbar_msg_info_wpt ( l, wpt );
2466 /* Mark for redraw */
2473 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2482 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2487 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2492 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2497 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2499 return l->waypoints;
2502 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2504 return ! ( g_hash_table_size ( vtl->tracks ) ||
2505 g_hash_table_size ( vtl->routes ) ||
2506 g_hash_table_size ( vtl->waypoints ) );
2510 * ATM use a case sensitive find
2511 * Finds the first one
2513 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2515 if ( wp && wp->name )
2516 if ( ! strcmp ( wp->name, name ) )
2522 * Get waypoint by name - not guaranteed to be unique
2523 * Finds the first one
2525 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2527 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2531 * ATM use a case sensitive find
2532 * Finds the first one
2534 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2536 if ( trk && trk->name )
2537 if ( ! strcmp ( trk->name, name ) )
2543 * Get track by name - not guaranteed to be unique
2544 * Finds the first one
2546 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2548 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2552 * Get route by name - not guaranteed to be unique
2553 * Finds the first one
2555 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2557 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2560 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] )
2562 static VikCoord fixme;
2563 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2564 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2565 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2566 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2567 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2568 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2569 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2570 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2571 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2574 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2576 GList *tr = trk->trackpoints;
2577 static VikCoord fixme;
2581 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2582 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2583 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2584 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2585 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2586 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2587 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2588 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2589 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2594 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2596 // Continually reuse maxmin to find the latest maximum and minimum values
2597 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2598 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2599 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2602 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2604 /* 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... */
2605 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2606 trw_layer_find_maxmin (vtl, maxmin);
2607 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2611 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2612 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2617 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2620 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2621 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2623 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2626 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2628 /* First set the center [in case previously viewing from elsewhere] */
2629 /* Then loop through zoom levels until provided positions are in view */
2630 /* This method is not particularly fast - but should work well enough */
2631 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2633 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2634 vik_viewport_set_center_coord ( vvp, &coord );
2636 /* Convert into definite 'smallest' and 'largest' positions */
2637 struct LatLon minmin;
2638 if ( maxmin[0].lat < maxmin[1].lat )
2639 minmin.lat = maxmin[0].lat;
2641 minmin.lat = maxmin[1].lat;
2643 struct LatLon maxmax;
2644 if ( maxmin[0].lon > maxmin[1].lon )
2645 maxmax.lon = maxmin[0].lon;
2647 maxmax.lon = maxmin[1].lon;
2649 /* Never zoom in too far - generally not that useful, as too close ! */
2650 /* Always recalculate the 'best' zoom level */
2652 vik_viewport_set_zoom ( vvp, zoom );
2654 gdouble min_lat, max_lat, min_lon, max_lon;
2655 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2656 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2657 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2658 /* NB I think the logic used in this test to determine if the bounds is within view
2659 fails if track goes across 180 degrees longitude.
2660 Hopefully that situation is not too common...
2661 Mind you viking doesn't really do edge locations to well anyway */
2662 if ( min_lat < minmin.lat &&
2663 max_lat > minmin.lat &&
2664 min_lon < maxmax.lon &&
2665 max_lon > maxmax.lon )
2666 /* Found within zoom level */
2671 vik_viewport_set_zoom ( vvp, zoom );
2675 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2677 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2678 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2679 trw_layer_find_maxmin (vtl, maxmin);
2680 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2683 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2688 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2690 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])) ) ) {
2691 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2694 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2697 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2699 GtkWidget *file_selector;
2701 gboolean failed = FALSE;
2702 file_selector = gtk_file_chooser_dialog_new (title,
2704 GTK_FILE_CHOOSER_ACTION_SAVE,
2705 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2706 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2708 gchar *cwd = g_get_current_dir();
2710 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
2714 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2716 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2718 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2719 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2721 gtk_widget_hide ( file_selector );
2722 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2723 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2724 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2729 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2731 gtk_widget_hide ( file_selector );
2732 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2733 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2734 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2739 gtk_widget_destroy ( file_selector );
2741 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2744 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2746 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2749 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2751 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2754 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2756 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2757 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2758 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2759 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2761 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2763 g_free ( auto_save_name );
2766 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2768 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2769 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2770 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2771 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2773 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2775 g_free ( auto_save_name );
2779 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2782 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2784 gchar *name_used = NULL;
2787 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2788 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2789 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2790 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2792 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2796 gchar *quoted_file = g_shell_quote ( name_used );
2797 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2798 g_free ( quoted_file );
2799 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2801 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2802 g_error_free ( err );
2806 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2807 //g_remove ( name_used );
2808 // Perhaps should be deleted when the program ends?
2809 // For now leave it to the user to delete it / use system temp cleanup methods.
2810 g_free ( name_used );
2814 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2816 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2819 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2821 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2824 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2826 gpointer layer_and_vlp[2];
2827 layer_and_vlp[0] = pass_along[0];
2828 layer_and_vlp[1] = pass_along[1];
2830 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2832 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2833 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2835 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2837 if ( !trk || !trk->name )
2840 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2841 gchar *auto_save_name = g_strdup ( trk->name );
2842 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2843 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2845 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2847 g_free ( auto_save_name );
2851 VikWaypoint *wp; // input
2852 gpointer uuid; // output
2855 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2857 wpu_udata *user_data = udata;
2858 if ( wp == user_data->wp ) {
2859 user_data->uuid = id;
2865 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2867 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2868 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2869 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2871 GTK_RESPONSE_REJECT,
2873 GTK_RESPONSE_ACCEPT,
2876 GtkWidget *label, *entry;
2877 label = gtk_label_new(_("Waypoint Name:"));
2878 entry = gtk_entry_new();
2880 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
2881 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
2882 gtk_widget_show_all ( label );
2883 gtk_widget_show_all ( entry );
2885 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2887 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2889 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2890 // Find *first* wp with the given name
2891 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2894 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2897 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2898 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2900 // Find and select on the side panel
2905 // Hmmm, want key of it
2906 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2908 if ( wpf && udata.uuid ) {
2909 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2910 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2919 gtk_widget_destroy ( dia );
2922 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2924 gchar *default_name = highest_wp_number_get(vtl);
2925 VikWaypoint *wp = vik_waypoint_new();
2926 gchar *returned_name;
2928 wp->coord = *def_coord;
2930 // Attempt to auto set height if DEM data is available
2931 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2932 if ( elev != VIK_DEM_INVALID_ELEVATION )
2933 wp->altitude = (gdouble)elev;
2935 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
2937 if ( returned_name )
2940 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2941 g_free (default_name);
2942 g_free (returned_name);
2945 g_free (default_name);
2946 vik_waypoint_free(wp);
2950 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2952 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2953 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2954 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2955 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2956 VikViewport *vvp = vik_window_viewport(vw);
2958 // Note the order is max part first then min part - thus reverse order of use in min_max function:
2959 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
2960 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2961 trw_layer_calculate_bounds_waypoints ( vtl );
2962 vik_layers_panel_emit_update ( vlp );
2965 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2967 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2968 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2969 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2971 trw_layer_find_maxmin (vtl, maxmin);
2972 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2973 trw_layer_calculate_bounds_waypoints ( vtl );
2974 vik_layers_panel_emit_update ( vlp );
2977 #ifdef VIK_CONFIG_GEOTAG
2978 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2980 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2982 // Update directly - not changing the mtime
2983 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2986 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2988 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2991 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2995 * Use code in separate file for this feature as reasonably complex
2997 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2999 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3000 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3001 // Unset so can be reverified later if necessary
3002 vtl->has_verified_thumbnails = FALSE;
3004 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3010 static void trw_layer_geotagging ( gpointer lav[2] )
3012 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3013 // Unset so can be reverified later if necessary
3014 vtl->has_verified_thumbnails = FALSE;
3016 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3023 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3026 * Acquire into this TRW Layer straight from GPS Device
3028 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3030 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3031 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3032 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3033 VikViewport *vvp = vik_window_viewport(vw);
3035 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3036 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3039 #ifdef VIK_CONFIG_GOOGLE
3041 * Acquire into this TRW Layer from Google Directions
3043 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
3045 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3046 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3047 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3048 VikViewport *vvp = vik_window_viewport(vw);
3050 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface, NULL, NULL );
3054 #ifdef VIK_CONFIG_OPENSTREETMAP
3056 * Acquire into this TRW Layer from OSM
3058 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3060 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3061 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3062 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3063 VikViewport *vvp = vik_window_viewport(vw);
3065 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3069 * Acquire into this TRW Layer from OSM for 'My' Traces
3071 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3073 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3074 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3075 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3076 VikViewport *vvp = vik_window_viewport(vw);
3078 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3082 #ifdef VIK_CONFIG_GEOCACHES
3084 * Acquire into this TRW Layer from Geocaching.com
3086 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3088 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3089 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3090 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3091 VikViewport *vvp = vik_window_viewport(vw);
3093 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3097 #ifdef VIK_CONFIG_GEOTAG
3099 * Acquire into this TRW Layer from images
3101 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3103 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3104 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3105 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3106 VikViewport *vvp = vik_window_viewport(vw);
3108 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3109 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3111 // Reverify thumbnails as they may have changed
3112 vtl->has_verified_thumbnails = FALSE;
3113 trw_layer_verify_thumbnails ( vtl, NULL );
3117 static void trw_layer_gps_upload ( gpointer lav[2] )
3119 gpointer pass_along[6];
3120 pass_along[0] = lav[0];
3121 pass_along[1] = lav[1];
3122 pass_along[2] = NULL; // No track - operate on the layer
3123 pass_along[3] = NULL;
3124 pass_along[4] = NULL;
3125 pass_along[5] = NULL;
3127 trw_layer_gps_upload_any ( pass_along );
3131 * If pass_along[3] is defined that this will upload just that track
3133 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3135 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3136 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3138 // May not actually get a track here as pass_along[2&3] can be null
3139 VikTrack *track = NULL;
3140 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3141 gboolean xfer_all = FALSE;
3143 if ( pass_along[2] ) {
3145 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3146 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3149 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3150 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3153 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3156 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3160 else if ( !pass_along[4] )
3161 xfer_all = TRUE; // i.e. whole layer
3163 if (track && !track->visible) {
3164 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3168 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3169 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3170 GTK_DIALOG_DESTROY_WITH_PARENT,
3172 GTK_RESPONSE_ACCEPT,
3174 GTK_RESPONSE_REJECT,
3177 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3178 GtkWidget *response_w = NULL;
3179 #if GTK_CHECK_VERSION (2, 20, 0)
3180 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3184 gtk_widget_grab_focus ( response_w );
3186 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3188 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3189 datasource_gps_clean_up ( dgs );
3190 gtk_widget_destroy ( dialog );
3194 // Get info from reused datasource dialog widgets
3195 gchar* protocol = datasource_gps_get_protocol ( dgs );
3196 gchar* port = datasource_gps_get_descriptor ( dgs );
3197 // NB don't free the above strings as they're references to values held elsewhere
3198 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3199 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3200 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3201 gboolean turn_off = datasource_gps_get_off ( dgs );
3203 gtk_widget_destroy ( dialog );
3205 // When called from the viewport - work the corresponding layerspanel:
3207 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3210 // Apply settings to transfer to the GPS device
3217 vik_layers_panel_get_viewport (vlp),
3226 * Acquire into this TRW Layer from any GPS Babel supported file
3228 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3230 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3231 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3232 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3233 VikViewport *vvp = vik_window_viewport(vw);
3235 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3238 static void trw_layer_new_wp ( gpointer lav[2] )
3240 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3241 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3242 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3243 instead return true if you want to update. */
3244 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 ) {
3245 trw_layer_calculate_bounds_waypoints ( vtl );
3246 vik_layers_panel_emit_update ( vlp );
3250 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3252 vtl->current_track = vik_track_new();
3253 vtl->current_track->visible = TRUE;
3254 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3255 // Create track with the preferred colour from the layer properties
3256 vtl->current_track->color = vtl->track_color;
3258 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3259 vtl->current_track->has_color = TRUE;
3260 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3263 static void trw_layer_new_track ( gpointer lav[2] )
3265 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3267 if ( ! vtl->current_track ) {
3268 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3269 new_track_create_common ( vtl, name );
3271 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3275 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3277 vtl->current_track = vik_track_new();
3278 vtl->current_track->visible = TRUE;
3279 vtl->current_track->is_route = TRUE;
3280 // By default make all routes red
3281 vtl->current_track->has_color = TRUE;
3282 gdk_color_parse ( "red", &vtl->current_track->color );
3283 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3286 static void trw_layer_new_route ( gpointer lav[2] )
3288 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3290 if ( ! vtl->current_track ) {
3291 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3292 new_route_create_common ( vtl, name );
3293 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3297 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3299 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3300 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3302 if ( g_hash_table_size (vtl->routes) > 0 ) {
3303 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3304 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3305 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3306 vik_layers_panel_emit_update ( vlp );
3311 static void trw_layer_finish_track ( gpointer lav[2] )
3313 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3314 vtl->current_track = NULL;
3315 vik_layer_emit_update ( VIK_LAYER(vtl) );
3318 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3320 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3321 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3323 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3324 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3325 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3326 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3327 vik_layers_panel_emit_update ( vlp );
3331 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3333 /* NB do not care if wp is visible or not */
3334 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3337 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3339 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3340 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3342 /* Only 1 waypoint - jump straight to it */
3343 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3344 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3345 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3347 /* If at least 2 waypoints - find center and then zoom to fit */
3348 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3350 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3351 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3352 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3355 vik_layers_panel_emit_update ( vlp );
3358 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3360 static gpointer pass_along[2];
3362 GtkWidget *export_submenu;
3363 pass_along[0] = vtl;
3364 pass_along[1] = vlp;
3366 item = gtk_menu_item_new();
3367 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3368 gtk_widget_show ( item );
3370 if ( vtl->current_track ) {
3371 if ( vtl->current_track->is_route )
3372 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3374 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3376 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3377 gtk_widget_show ( item );
3380 item = gtk_menu_item_new ();
3381 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3382 gtk_widget_show ( item );
3385 /* Now with icons */
3386 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3387 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3388 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3389 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3390 gtk_widget_show ( item );
3392 GtkWidget *view_submenu = gtk_menu_new();
3393 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3394 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3395 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3396 gtk_widget_show ( item );
3397 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3399 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3400 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3401 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3402 gtk_widget_show ( item );
3404 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3405 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3406 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3407 gtk_widget_show ( item );
3409 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3410 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3411 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3412 gtk_widget_show ( item );
3414 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3415 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3416 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3417 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3418 gtk_widget_show ( item );
3420 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3421 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3422 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3423 gtk_widget_show ( item );
3425 export_submenu = gtk_menu_new ();
3426 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3427 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3428 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3429 gtk_widget_show ( item );
3430 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3432 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3433 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3434 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3435 gtk_widget_show ( item );
3437 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3438 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3439 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3440 gtk_widget_show ( item );
3442 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3443 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3444 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3445 gtk_widget_show ( item );
3447 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3449 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3450 gtk_widget_show ( item );
3452 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3453 item = gtk_menu_item_new_with_mnemonic ( external1 );
3454 g_free ( external1 );
3455 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3456 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3457 gtk_widget_show ( item );
3459 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3460 item = gtk_menu_item_new_with_mnemonic ( external2 );
3461 g_free ( external2 );
3462 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3463 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3464 gtk_widget_show ( item );
3466 GtkWidget *new_submenu = gtk_menu_new();
3467 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3468 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3469 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3470 gtk_widget_show(item);
3471 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3473 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3474 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3475 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3476 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3477 gtk_widget_show ( item );
3479 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3480 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3481 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3482 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3483 gtk_widget_show ( item );
3484 // Make it available only when a new track *not* already in progress
3485 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3487 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3488 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3490 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3491 gtk_widget_show ( item );
3492 // Make it available only when a new track *not* already in progress
3493 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3495 #ifdef VIK_CONFIG_GEOTAG
3496 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3497 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3498 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3499 gtk_widget_show ( item );
3502 GtkWidget *acquire_submenu = gtk_menu_new ();
3503 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3504 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3505 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3506 gtk_widget_show ( item );
3507 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3509 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3511 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3512 gtk_widget_show ( item );
3514 #ifdef VIK_CONFIG_GOOGLE
3515 item = gtk_menu_item_new_with_mnemonic ( _("From Google _Directions...") );
3516 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3517 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3518 gtk_widget_show ( item );
3521 #ifdef VIK_CONFIG_OPENSTREETMAP
3522 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3524 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3525 gtk_widget_show ( item );
3527 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3529 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3530 gtk_widget_show ( item );
3533 #ifdef VIK_CONFIG_GEONAMES
3534 GtkWidget *wikipedia_submenu = gtk_menu_new();
3535 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3536 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3537 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3538 gtk_widget_show(item);
3539 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3541 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3542 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3543 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3544 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3545 gtk_widget_show ( item );
3547 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3548 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3550 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3551 gtk_widget_show ( item );
3554 #ifdef VIK_CONFIG_GEOCACHES
3555 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3556 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3557 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3558 gtk_widget_show ( item );
3561 #ifdef VIK_CONFIG_GEOTAG
3562 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3563 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3564 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3565 gtk_widget_show ( item );
3568 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3569 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3570 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3571 gtk_widget_show ( item );
3573 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3575 GtkWidget *upload_submenu = gtk_menu_new ();
3576 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3577 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3578 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3579 gtk_widget_show ( item );
3580 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3582 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3583 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3584 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3585 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3586 gtk_widget_show ( item );
3588 #ifdef VIK_CONFIG_OPENSTREETMAP
3589 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3590 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3591 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3592 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3593 gtk_widget_show ( item );
3596 GtkWidget *delete_submenu = gtk_menu_new ();
3597 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3598 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3599 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3600 gtk_widget_show ( item );
3601 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3603 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3604 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3605 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3606 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3607 gtk_widget_show ( item );
3609 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3610 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3612 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3613 gtk_widget_show ( item );
3615 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3616 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3617 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3618 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3619 gtk_widget_show ( item );
3621 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3622 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3623 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3624 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3625 gtk_widget_show ( item );
3627 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3628 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3630 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3631 gtk_widget_show ( item );
3633 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3634 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3635 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3636 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3637 gtk_widget_show ( item );
3639 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3640 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3642 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3643 gtk_widget_show ( item );
3646 item = a_acquire_trwlayer_track_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 );
3654 // Fake Waypoint UUIDs vi simple increasing integer
3655 static guint wp_uuid = 0;
3657 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3661 vik_waypoint_set_name (wp, name);
3663 if ( VIK_LAYER(vtl)->realized )
3665 // Do we need to create the sublayer:
3666 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3667 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3670 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3672 // Visibility column always needed for waypoints
3673 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3674 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 );
3676 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 );
3678 // Actual setting of visibility dependent on the waypoint
3679 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3681 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3684 highest_wp_number_add_wp(vtl, name);
3685 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3689 // Fake Track UUIDs vi simple increasing integer
3690 static guint tr_uuid = 0;
3692 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3696 vik_track_set_name (t, name);
3698 if ( VIK_LAYER(vtl)->realized )
3700 // Do we need to create the sublayer:
3701 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3702 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3705 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3706 // Visibility column always needed for tracks
3707 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3708 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 );
3710 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 );
3712 // Actual setting of visibility dependent on the track
3713 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3715 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3718 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3720 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3723 // Fake Route UUIDs vi simple increasing integer
3724 static guint rt_uuid = 0;
3726 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3730 vik_track_set_name (t, name);
3732 if ( VIK_LAYER(vtl)->realized )
3734 // Do we need to create the sublayer:
3735 if ( g_hash_table_size (vtl->routes) == 0 ) {
3736 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3739 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3740 // Visibility column always needed for tracks
3741 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3742 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 );
3744 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 );
3746 // Actual setting of visibility dependent on the track
3747 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3749 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3752 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3754 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3757 /* to be called whenever a track has been deleted or may have been changed. */
3758 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3760 if (vtl->current_tp_track == trk )
3761 trw_layer_cancel_current_tp ( vtl, FALSE );
3765 * Normally this is done to due the waypoint size preference having changed
3767 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
3769 GHashTableIter iter;
3770 gpointer key, value;
3773 g_hash_table_iter_init ( &iter, vtl->waypoints );
3774 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
3775 VikWaypoint *wp = VIK_WAYPOINT(value);
3777 // Reapply symbol setting to update the pixbuf
3778 gchar *tmp_symbol = g_strdup ( wp->symbol );
3779 vik_waypoint_set_symbol ( wp, tmp_symbol );
3780 g_free ( tmp_symbol );
3785 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3788 gchar *newname = g_strdup(name);
3793 switch ( sublayer_type ) {
3794 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3795 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3797 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3798 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3801 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3804 // If found a name already in use try adding 1 to it and we try again
3806 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3808 newname = new_newname;
3811 } while ( id != NULL);
3816 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3818 // No more uniqueness of name forced when loading from a file
3819 // This now makes this function a little redunant as we just flow the parameters through
3820 vik_trw_layer_add_waypoint ( vtl, name, wp );
3823 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3825 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3826 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3827 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3828 vik_track_free ( tr );
3829 vtl->route_finder_append = FALSE; /* this means we have added it */
3832 // No more uniqueness of name forced when loading from a file
3834 vik_trw_layer_add_route ( vtl, name, tr );
3836 vik_trw_layer_add_track ( vtl, name, tr );
3838 if ( vtl->route_finder_check_added_track ) {
3839 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3840 vtl->route_finder_added_track = tr;
3845 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3847 *l = g_list_append(*l, id);
3851 * Move an item from one TRW layer to another TRW layer
3853 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3855 gboolean rename = ( vtl_src != vtl_dest );
3857 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3858 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3862 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3864 newname = g_strdup ( trk->name );
3866 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3867 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3868 vik_trw_layer_delete_track ( vtl_src, trk );
3871 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3872 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3876 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3878 newname = g_strdup ( trk->name );
3880 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3881 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3882 vik_trw_layer_delete_route ( vtl_src, trk );
3885 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3886 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3890 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
3892 newname = g_strdup ( wp->name );
3894 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3895 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3896 trw_layer_delete_waypoint ( vtl_src, wp );
3898 // Recalculate bounds even if not renamed as maybe dragged between layers
3899 trw_layer_calculate_bounds_waypoints ( vtl_dest );
3900 trw_layer_calculate_bounds_waypoints ( vtl_src );
3904 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3906 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3907 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3909 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3910 GList *items = NULL;
3913 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3914 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3916 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3917 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3919 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3920 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3925 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3926 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3928 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3929 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3931 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3938 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3939 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3944 VikTrack *trk; // input
3945 gpointer uuid; // output
3948 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3950 trku_udata *user_data = udata;
3951 if ( trk == user_data->trk ) {
3952 user_data->uuid = id;
3958 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3960 gboolean was_visible = FALSE;
3962 if ( trk && trk->name ) {
3964 if ( trk == vtl->current_track ) {
3965 vtl->current_track = NULL;
3966 vtl->current_tp_track = NULL;
3967 vtl->current_tp_id = NULL;
3968 vtl->moving_tp = FALSE;
3971 was_visible = trk->visible;
3973 if ( trk == vtl->route_finder_current_track )
3974 vtl->route_finder_current_track = NULL;
3976 if ( trk == vtl->route_finder_added_track )
3977 vtl->route_finder_added_track = NULL;
3983 // Hmmm, want key of it
3984 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3986 if ( trkf && udata.uuid ) {
3987 /* could be current_tp, so we have to check */
3988 trw_layer_cancel_tps_of_track ( vtl, trk );
3990 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3993 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3994 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3995 g_hash_table_remove ( vtl->tracks, udata.uuid );
3997 // If last sublayer, then remove sublayer container
3998 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3999 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4007 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4009 gboolean was_visible = FALSE;
4011 if ( trk && trk->name ) {
4013 if ( trk == vtl->current_track ) {
4014 vtl->current_track = NULL;
4015 vtl->current_tp_track = NULL;
4016 vtl->current_tp_id = NULL;
4017 vtl->moving_tp = FALSE;
4020 was_visible = trk->visible;
4022 if ( trk == vtl->route_finder_current_track )
4023 vtl->route_finder_current_track = NULL;
4025 if ( trk == vtl->route_finder_added_track )
4026 vtl->route_finder_added_track = NULL;
4032 // Hmmm, want key of it
4033 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4035 if ( trkf && udata.uuid ) {
4036 /* could be current_tp, so we have to check */
4037 trw_layer_cancel_tps_of_track ( vtl, trk );
4039 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4042 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4043 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4044 g_hash_table_remove ( vtl->routes, udata.uuid );
4046 // If last sublayer, then remove sublayer container
4047 if ( g_hash_table_size (vtl->routes) == 0 ) {
4048 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4056 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4058 gboolean was_visible = FALSE;
4060 if ( wp && wp->name ) {
4062 if ( wp == vtl->current_wp ) {
4063 vtl->current_wp = NULL;
4064 vtl->current_wp_id = NULL;
4065 vtl->moving_wp = FALSE;
4068 was_visible = wp->visible;
4074 // Hmmm, want key of it
4075 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4077 if ( wpf && udata.uuid ) {
4078 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4081 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4082 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4084 highest_wp_number_remove_wp(vtl, wp->name);
4085 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4087 // If last sublayer, then remove sublayer container
4088 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4089 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4099 // Only for temporary use by trw_layer_delete_waypoint_by_name
4100 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4102 wpu_udata *user_data = udata;
4103 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4104 user_data->uuid = id;
4111 * Delete a waypoint by the given name
4112 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4113 * as there be multiple waypoints with the same name
4115 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4118 // Fake a waypoint with the given name
4119 udata.wp = vik_waypoint_new ();
4120 vik_waypoint_set_name (udata.wp, name);
4121 // Currently only the name is used in this waypoint find function
4124 // Hmmm, want key of it
4125 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4127 vik_waypoint_free (udata.wp);
4129 if ( wpf && udata.uuid )
4130 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4136 VikTrack *trk; // input
4137 gpointer uuid; // output
4140 // Only for temporary use by trw_layer_delete_track_by_name
4141 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4143 tpu_udata *user_data = udata;
4144 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4145 user_data->uuid = id;
4152 * Delete a track by the given name
4153 * NOTE: ATM this will delete the first encountered Track with the specified name
4154 * as there may be multiple tracks with the same name within the specified hash table
4156 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4159 // Fake a track with the given name
4160 udata.trk = vik_track_new ();
4161 vik_track_set_name (udata.trk, name);
4162 // Currently only the name is used in this waypoint find function
4165 // Hmmm, want key of it
4166 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4168 vik_track_free (udata.trk);
4170 if ( trkf && udata.uuid ) {
4171 // This could be a little better written...
4172 if ( vtl->tracks == ht_tracks )
4173 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4174 if ( vtl->routes == ht_tracks )
4175 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4182 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4184 vik_treeview_item_delete (vt, it );
4187 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4190 vtl->current_track = NULL;
4191 vtl->route_finder_current_track = NULL;
4192 vtl->route_finder_added_track = NULL;
4193 if (vtl->current_tp_track)
4194 trw_layer_cancel_current_tp(vtl, FALSE);
4196 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4197 g_hash_table_remove_all(vtl->routes_iters);
4198 g_hash_table_remove_all(vtl->routes);
4200 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4202 vik_layer_emit_update ( VIK_LAYER(vtl) );
4205 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4208 vtl->current_track = NULL;
4209 vtl->route_finder_current_track = NULL;
4210 vtl->route_finder_added_track = NULL;
4211 if (vtl->current_tp_track)
4212 trw_layer_cancel_current_tp(vtl, FALSE);
4214 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4215 g_hash_table_remove_all(vtl->tracks_iters);
4216 g_hash_table_remove_all(vtl->tracks);
4218 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4220 vik_layer_emit_update ( VIK_LAYER(vtl) );
4223 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4225 vtl->current_wp = NULL;
4226 vtl->current_wp_id = NULL;
4227 vtl->moving_wp = FALSE;
4229 highest_wp_number_reset(vtl);
4231 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4232 g_hash_table_remove_all(vtl->waypoints_iters);
4233 g_hash_table_remove_all(vtl->waypoints);
4235 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4237 vik_layer_emit_update ( VIK_LAYER(vtl) );
4240 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4242 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4243 // Get confirmation from the user
4244 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4245 _("Are you sure you want to delete all tracks in %s?"),
4246 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4247 vik_trw_layer_delete_all_tracks (vtl);
4250 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4252 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4253 // Get confirmation from the user
4254 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4255 _("Are you sure you want to delete all routes in %s?"),
4256 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4257 vik_trw_layer_delete_all_routes (vtl);
4260 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4262 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4263 // Get confirmation from the user
4264 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4265 _("Are you sure you want to delete all waypoints in %s?"),
4266 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4267 vik_trw_layer_delete_all_waypoints (vtl);
4270 static void trw_layer_delete_item ( gpointer pass_along[6] )
4272 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4273 gboolean was_visible = FALSE;
4274 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4276 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4277 if ( wp && wp->name ) {
4278 if ( GPOINTER_TO_INT ( pass_along[4]) )
4279 // Get confirmation from the user
4280 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4281 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4282 _("Are you sure you want to delete the waypoint \"%s\""),
4285 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4288 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4290 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4291 if ( trk && trk->name ) {
4292 if ( GPOINTER_TO_INT ( pass_along[4]) )
4293 // Get confirmation from the user
4294 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4295 _("Are you sure you want to delete the track \"%s\""),
4298 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4303 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4304 if ( trk && trk->name ) {
4305 if ( GPOINTER_TO_INT ( pass_along[4]) )
4306 // Get confirmation from the user
4307 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4308 _("Are you sure you want to delete the route \"%s\""),
4311 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4315 vik_layer_emit_update ( VIK_LAYER(vtl) );
4319 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4321 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4323 vik_waypoint_set_name ( wp, new_name );
4325 // Now update the treeview as well
4330 // Need key of it for treeview update
4331 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4333 if ( wpf && udataU.uuid ) {
4334 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4337 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4338 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4339 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, new_name );
4345 static void trw_layer_properties_item ( gpointer pass_along[7] )
4347 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4348 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4350 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4352 if ( wp && wp->name )
4354 gboolean updated = FALSE;
4355 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4357 trw_layer_waypoint_rename ( vtl, wp, new_name );
4359 if ( updated && pass_along[6] )
4360 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4362 if ( updated && VIK_LAYER(vtl)->visible )
4363 vik_layer_emit_update ( VIK_LAYER(vtl) );
4369 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4370 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4372 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4374 if ( tr && tr->name )
4376 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4379 pass_along[1], /* vlp */
4380 pass_along[5], /* vvp */
4381 pass_along[6]); /* iter */
4387 * Update the treeview of the track id - primarily to update the icon
4389 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4395 gpointer *trkf = NULL;
4396 if ( trk->is_route )
4397 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4399 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4401 if ( trkf && udata.uuid ) {
4403 GtkTreeIter *iter = NULL;
4404 if ( trk->is_route )
4405 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4407 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4410 // TODO: Make this a function
4411 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4412 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4413 ((trk->color.green & 0xff00) << 8) |
4414 (trk->color.blue & 0xff00);
4415 gdk_pixbuf_fill ( pixbuf, pixel );
4416 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4417 g_object_unref (pixbuf);
4424 Parameter 1 -> VikLayersPanel
4425 Parameter 2 -> VikLayer
4426 Parameter 3 -> VikViewport
4428 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4431 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4432 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4435 /* since vlp not set, vl & vvp should be valid instead! */
4437 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4438 vik_layer_emit_update ( VIK_LAYER(vl) );
4443 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4445 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4447 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4448 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4450 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4452 if ( track && track->trackpoints )
4453 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4456 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4458 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4460 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4461 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4463 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4465 if ( track && track->trackpoints )
4467 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4469 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4470 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4471 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4472 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4473 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4477 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4479 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4481 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4482 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4484 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4489 // Converting a track to a route can be a bit more complicated,
4490 // so give a chance to change our minds:
4491 if ( !trk->is_route &&
4492 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4493 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4495 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4496 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4501 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4504 trk_copy->is_route = !trk_copy->is_route;
4506 // ATM can't set name to self - so must create temporary copy
4507 gchar *name = g_strdup ( trk_copy->name );
4509 // Delete old one and then add new one
4510 if ( trk->is_route ) {
4511 vik_trw_layer_delete_route ( vtl, trk );
4512 vik_trw_layer_add_track ( vtl, name, trk_copy );
4515 // Extra route conversion bits...
4516 vik_track_merge_segments ( trk_copy );
4517 vik_track_to_routepoints ( trk_copy );
4519 vik_trw_layer_delete_track ( vtl, trk );
4520 vik_trw_layer_add_route ( vtl, name, trk_copy );
4524 // Update in case color of track / route changes when moving between sublayers
4525 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4529 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4531 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4533 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4534 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4536 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4541 vtl->current_track = track;
4542 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);
4544 if ( track->trackpoints )
4545 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4549 * extend a track using route finder
4551 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4553 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4554 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4557 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4559 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4560 vtl->route_finder_coord = last_coord;
4561 vtl->route_finder_current_track = track;
4562 vtl->route_finder_started = TRUE;
4564 if ( track->trackpoints )
4565 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4569 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4571 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4572 /* Also warn if overwrite old elevation data */
4573 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4575 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4576 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4578 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4581 vik_track_apply_dem_data ( track );
4584 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4586 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4588 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4589 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4591 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4596 GList *trps = track->trackpoints;
4599 trps = g_list_last(trps);
4600 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4603 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4605 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4607 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4608 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4610 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4615 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4618 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4621 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4623 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4625 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4626 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4628 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4633 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4636 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4639 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4641 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4643 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4644 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4646 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4651 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4654 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4658 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4660 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4662 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4664 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4665 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4667 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4669 if ( trk && trk->trackpoints )
4671 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4672 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4673 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4674 if ( pass_along[1] )
4675 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4677 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4681 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4683 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4684 trw_layer_tpwin_init ( vtl );
4687 /*************************************
4688 * merge/split by time routines
4689 *************************************/
4691 /* called for each key in track hash table.
4692 * If the current track has the same time stamp type, add it to the result,
4693 * except the one pointed by "exclude".
4694 * set exclude to NULL if there is no exclude to check.
4695 * Note that the result is in reverse (for performance reasons).
4700 gboolean with_timestamps;
4702 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4704 twt_udata *user_data = udata;
4705 VikTrackpoint *p1, *p2;
4707 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4711 if (VIK_TRACK(value)->trackpoints) {
4712 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4713 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4715 if ( user_data->with_timestamps ) {
4716 if (!p1->has_timestamp || !p2->has_timestamp) {
4721 // Don't add tracks with timestamps when getting non timestamp tracks
4722 if (p1->has_timestamp || p2->has_timestamp) {
4728 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4731 /* called for each key in track hash table. if original track user_data[1] is close enough
4732 * to the passed one, add it to list in user_data[0]
4734 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4737 VikTrackpoint *p1, *p2;
4738 VikTrack *trk = VIK_TRACK(value);
4740 GList **nearby_tracks = ((gpointer *)user_data)[0];
4741 GList *tpoints = ((gpointer *)user_data)[1];
4744 * detect reasons for not merging, and return
4745 * if no reason is found not to merge, then do it.
4748 // Exclude the original track from the compiled list
4749 if (trk->trackpoints == tpoints) {
4753 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4754 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4756 if (trk->trackpoints) {
4757 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4758 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4760 if (!p1->has_timestamp || !p2->has_timestamp) {
4761 //g_print("no timestamp\n");
4765 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4766 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4767 if (! (abs(t1 - p2->timestamp) < threshold ||
4769 abs(p1->timestamp - t2) < threshold)
4776 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4779 /* comparison function used to sort tracks; a and b are hash table keys */
4780 /* Not actively used - can be restored if needed
4781 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4783 GHashTable *tracks = user_data;
4786 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4787 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4789 if (t1 < t2) return -1;
4790 if (t1 > t2) return 1;
4795 /* comparison function used to sort trackpoints */
4796 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4798 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4800 if (t1 < t2) return -1;
4801 if (t1 > t2) return 1;
4806 * comparison function which can be used to sort tracks or waypoints by name
4808 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4810 const gchar* namea = (const gchar*) a;
4811 const gchar* nameb = (const gchar*) b;
4812 if ( namea == NULL || nameb == NULL)
4815 // Same sort method as used in the vik_treeview_*_alphabetize functions
4816 return strcmp ( namea, nameb );
4820 * Attempt to merge selected track with other tracks specified by the user
4821 * Tracks to merge with must be of the same 'type' as the selected track -
4822 * either all with timestamps, or all without timestamps
4824 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4826 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4827 GList *other_tracks = NULL;
4828 GHashTable *ght_tracks;
4829 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4830 ght_tracks = vtl->routes;
4832 ght_tracks = vtl->tracks;
4834 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4839 if ( !track->trackpoints )
4843 udata.result = &other_tracks;
4844 udata.exclude = track->trackpoints;
4845 // Allow merging with 'similar' time type time tracks
4846 // i.e. either those times, or those without
4847 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4849 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4850 other_tracks = g_list_reverse(other_tracks);
4852 if ( !other_tracks ) {
4853 if ( udata.with_timestamps )
4854 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4856 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4860 // Sort alphabetically for user presentation
4861 // Convert into list of names for usage with dialog function
4862 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4863 GList *other_tracks_names = NULL;
4864 GList *iter = g_list_first ( other_tracks );
4866 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4867 iter = g_list_next ( iter );
4870 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4872 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4876 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4877 g_list_free(other_tracks);
4878 g_list_free(other_tracks_names);
4883 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4884 VikTrack *merge_track;
4885 if ( track->is_route )
4886 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4888 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4891 vik_track_steal_and_append_trackpoints ( track, merge_track );
4892 if ( track->is_route )
4893 vik_trw_layer_delete_route (vtl, merge_track);
4895 vik_trw_layer_delete_track (vtl, merge_track);
4896 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4899 for (l = merge_list; l != NULL; l = g_list_next(l))
4901 g_list_free(merge_list);
4903 vik_layer_emit_update( VIK_LAYER(vtl) );
4907 // c.f. trw_layer_sorted_track_id_by_name_list
4908 // but don't add the specified track to the list (normally current track)
4909 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4911 twt_udata *user_data = udata;
4914 if (trk->trackpoints == user_data->exclude) {
4918 // Sort named list alphabetically
4919 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4923 * Join - this allows combining 'tracks' and 'track routes'
4924 * i.e. doesn't care about whether tracks have consistent timestamps
4925 * ATM can only append one track at a time to the currently selected track
4927 static void trw_layer_append_track ( gpointer pass_along[6] )
4930 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4932 GHashTable *ght_tracks;
4933 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4934 ght_tracks = vtl->routes;
4936 ght_tracks = vtl->tracks;
4938 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4943 GList *other_tracks_names = NULL;
4945 // Sort alphabetically for user presentation
4946 // Convert into list of names for usage with dialog function
4947 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4949 udata.result = &other_tracks_names;
4950 udata.exclude = trk->trackpoints;
4952 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4954 // Note the limit to selecting one track only
4955 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4956 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4957 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4960 trk->is_route ? _("Append Route"): _("Append Track"),
4961 trk->is_route ? _("Select the route to append after the current route") :
4962 _("Select the track to append after the current track") );
4964 g_list_free(other_tracks_names);
4966 // It's a list, but shouldn't contain more than one other track!
4967 if ( append_list ) {
4969 for (l = append_list; l != NULL; l = g_list_next(l)) {
4970 // TODO: at present this uses the first track found by name,
4971 // which with potential multiple same named tracks may not be the one selected...
4972 VikTrack *append_track;
4973 if ( trk->is_route )
4974 append_track = vik_trw_layer_get_route ( vtl, l->data );
4976 append_track = vik_trw_layer_get_track ( vtl, l->data );
4978 if ( append_track ) {
4979 vik_track_steal_and_append_trackpoints ( trk, append_track );
4980 if ( trk->is_route )
4981 vik_trw_layer_delete_route (vtl, append_track);
4983 vik_trw_layer_delete_track (vtl, append_track);
4986 for (l = append_list; l != NULL; l = g_list_next(l))
4988 g_list_free(append_list);
4990 vik_layer_emit_update( VIK_LAYER(vtl) );
4995 * Very similar to trw_layer_append_track for joining
4996 * but this allows selection from the 'other' list
4997 * If a track is selected, then is shows routes and joins the selected one
4998 * If a route is selected, then is shows tracks and joins the selected one
5000 static void trw_layer_append_other ( gpointer pass_along[6] )
5003 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5005 GHashTable *ght_mykind, *ght_others;
5006 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5007 ght_mykind = vtl->routes;
5008 ght_others = vtl->tracks;
5011 ght_mykind = vtl->tracks;
5012 ght_others = vtl->routes;
5015 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5020 GList *other_tracks_names = NULL;
5022 // Sort alphabetically for user presentation
5023 // Convert into list of names for usage with dialog function
5024 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5026 udata.result = &other_tracks_names;
5027 udata.exclude = trk->trackpoints;
5029 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5031 // Note the limit to selecting one track only
5032 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5033 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5034 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5037 trk->is_route ? _("Append Track"): _("Append Route"),
5038 trk->is_route ? _("Select the track to append after the current route") :
5039 _("Select the route to append after the current track") );
5041 g_list_free(other_tracks_names);
5043 // It's a list, but shouldn't contain more than one other track!
5044 if ( append_list ) {
5046 for (l = append_list; l != NULL; l = g_list_next(l)) {
5047 // TODO: at present this uses the first track found by name,
5048 // which with potential multiple same named tracks may not be the one selected...
5050 // Get FROM THE OTHER TYPE list
5051 VikTrack *append_track;
5052 if ( trk->is_route )
5053 append_track = vik_trw_layer_get_track ( vtl, l->data );
5055 append_track = vik_trw_layer_get_route ( vtl, l->data );
5057 if ( append_track ) {
5059 if ( !append_track->is_route &&
5060 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5061 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5063 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5064 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5065 vik_track_merge_segments ( append_track );
5066 vik_track_to_routepoints ( append_track );
5073 vik_track_steal_and_append_trackpoints ( trk, append_track );
5075 // Delete copied which is FROM THE OTHER TYPE list
5076 if ( trk->is_route )
5077 vik_trw_layer_delete_track (vtl, append_track);
5079 vik_trw_layer_delete_route (vtl, append_track);
5082 for (l = append_list; l != NULL; l = g_list_next(l))
5084 g_list_free(append_list);
5085 vik_layer_emit_update( VIK_LAYER(vtl) );
5089 /* merge by segments */
5090 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5092 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5093 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5094 guint segments = vik_track_merge_segments ( trk );
5095 // NB currently no need to redraw as segments not actually shown on the display
5096 // However inform the user of what happened:
5098 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5099 g_snprintf(str, 64, tmp_str, segments);
5100 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5103 /* merge by time routine */
5104 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5106 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5110 GList *tracks_with_timestamp = NULL;
5111 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5112 if (orig_trk->trackpoints &&
5113 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5114 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5119 udata.result = &tracks_with_timestamp;
5120 udata.exclude = orig_trk->trackpoints;
5121 udata.with_timestamps = TRUE;
5122 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5123 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5125 if (!tracks_with_timestamp) {
5126 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5129 g_list_free(tracks_with_timestamp);
5131 static guint threshold_in_minutes = 1;
5132 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5133 _("Merge Threshold..."),
5134 _("Merge when time between tracks less than:"),
5135 &threshold_in_minutes)) {
5139 // keep attempting to merge all tracks until no merges within the time specified is possible
5140 gboolean attempt_merge = TRUE;
5141 GList *nearby_tracks = NULL;
5143 static gpointer params[3];
5145 while ( attempt_merge ) {
5147 // Don't try again unless tracks have changed
5148 attempt_merge = FALSE;
5150 trps = orig_trk->trackpoints;
5154 if (nearby_tracks) {
5155 g_list_free(nearby_tracks);
5156 nearby_tracks = NULL;
5159 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5160 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5162 /* g_print("Original track times: %d and %d\n", t1, t2); */
5163 params[0] = &nearby_tracks;
5164 params[1] = (gpointer)trps;
5165 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5167 /* get a list of adjacent-in-time tracks */
5168 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5171 GList *l = nearby_tracks;
5174 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5175 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5177 t1 = get_first_trackpoint(l)->timestamp;
5178 t2 = get_last_trackpoint(l)->timestamp;
5179 #undef get_first_trackpoint
5180 #undef get_last_trackpoint
5181 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5184 /* remove trackpoints from merged track, delete track */
5185 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5186 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5188 // Tracks have changed, therefore retry again against all the remaining tracks
5189 attempt_merge = TRUE;
5194 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5197 g_list_free(nearby_tracks);
5199 vik_layer_emit_update( VIK_LAYER(vtl) );
5203 * Split a track at the currently selected trackpoint
5205 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5207 if ( !vtl->current_tpl )
5210 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5211 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5213 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5214 GList *newglist = g_list_alloc ();
5215 newglist->prev = NULL;
5216 newglist->next = vtl->current_tpl->next;
5217 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5218 tr->trackpoints = newglist;
5220 vtl->current_tpl->next->prev = newglist; /* end old track here */
5221 vtl->current_tpl->next = NULL;
5223 // Bounds of the selected track changed due to the split
5224 vik_track_calculate_bounds ( vtl->current_tp_track );
5226 vtl->current_tpl = newglist; /* change tp to first of new track. */
5227 vtl->current_tp_track = tr;
5230 vik_trw_layer_add_route ( vtl, name, tr );
5232 vik_trw_layer_add_track ( vtl, name, tr );
5234 // Bounds of the new track created by the split
5235 vik_track_calculate_bounds ( tr );
5241 // Also need id of newly created track
5244 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5246 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5248 if ( trkf && udata.uuid )
5249 vtl->current_tp_id = udata.uuid;
5251 vtl->current_tp_id = NULL;
5253 vik_layer_emit_update(VIK_LAYER(vtl));
5258 /* split by time routine */
5259 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5261 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5262 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5263 GList *trps = track->trackpoints;
5265 GList *newlists = NULL;
5266 GList *newtps = NULL;
5267 static guint thr = 1;
5274 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5275 _("Split Threshold..."),
5276 _("Split when time between trackpoints exceeds:"),
5281 /* iterate through trackpoints, and copy them into new lists without touching original list */
5282 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5286 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5288 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5291 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5292 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5293 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5295 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
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 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6672 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6673 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
6674 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6675 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6676 gtk_widget_show ( item );
6679 // ATM can't upload a single waypoint but can do waypoints to a GPS
6680 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6681 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6682 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6683 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6684 gtk_widget_show ( item );
6685 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6687 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6688 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6689 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6690 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6691 gtk_widget_show ( item );
6695 #ifdef VIK_CONFIG_GOOGLE
6696 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6698 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6699 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6700 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6701 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6702 gtk_widget_show ( item );
6706 // Some things aren't usable with routes
6707 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6708 #ifdef VIK_CONFIG_OPENSTREETMAP
6709 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6710 // Convert internal pointer into actual track for usage outside this file
6711 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6712 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6713 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6714 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6715 gtk_widget_show ( item );
6718 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6719 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6720 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6721 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6722 gtk_widget_show ( item );
6724 /* ATM This function is only available via the layers panel, due to needing a vlp */
6726 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6727 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6728 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6730 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6731 gtk_widget_show ( item );
6735 #ifdef VIK_CONFIG_GEOTAG
6736 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6738 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6739 gtk_widget_show ( item );
6743 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6744 // Only show on viewport popmenu when a trackpoint is selected
6745 if ( ! vlp && l->current_tpl ) {
6747 item = gtk_menu_item_new ();
6748 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6749 gtk_widget_show ( item );
6751 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6752 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6754 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6755 gtk_widget_show ( item );
6762 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6765 if (!vtl->current_tpl)
6767 if (!vtl->current_tpl->next)
6770 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6771 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6773 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6776 VikTrackpoint *tp_new = vik_trackpoint_new();
6777 struct LatLon ll_current, ll_next;
6778 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6779 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6781 /* main positional interpolation */
6782 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6783 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6785 /* Now other properties that can be interpolated */
6786 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6788 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6789 /* Note here the division is applied to each part, then added
6790 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6791 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6792 tp_new->has_timestamp = TRUE;
6795 if (tp_current->speed != NAN && tp_next->speed != NAN)
6796 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6798 /* TODO - improve interpolation of course, as it may not be correct.
6799 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6800 [similar applies if value is in radians] */
6801 if (tp_current->course != NAN && tp_next->course != NAN)
6802 tp_new->speed = (tp_current->course + tp_next->course)/2;
6804 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6806 /* Insert new point into the trackpoints list after the current TP */
6807 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6809 // Otherwise try routes
6810 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6814 gint index = g_list_index ( trk->trackpoints, tp_current );
6816 // NB no recalculation of bounds since it is inserted between points
6817 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6822 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6828 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6832 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6834 if ( vtl->current_tpl )
6836 vtl->current_tpl = NULL;
6837 vtl->current_tp_track = NULL;
6838 vtl->current_tp_id = NULL;
6839 vik_layer_emit_update(VIK_LAYER(vtl));
6843 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6845 g_assert ( vtl->tpwin != NULL );
6846 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6847 trw_layer_cancel_current_tp ( vtl, TRUE );
6849 if ( vtl->current_tpl == NULL )
6852 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6854 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6855 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6857 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6859 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6861 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6867 // Find available adjacent trackpoint
6868 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6870 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6871 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6873 // Delete current trackpoint
6874 vik_trackpoint_free ( vtl->current_tpl->data );
6875 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6877 // Set to current to the available adjacent trackpoint
6878 vtl->current_tpl = new_tpl;
6880 // Reset dialog with the available adjacent trackpoint
6881 if ( vtl->current_tp_track ) {
6882 vik_track_calculate_bounds ( vtl->current_tp_track );
6883 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6886 vik_layer_emit_update(VIK_LAYER(vtl));
6890 // Delete current trackpoint
6891 vik_trackpoint_free ( vtl->current_tpl->data );
6892 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6893 trw_layer_cancel_current_tp ( vtl, FALSE );
6896 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6898 if ( vtl->current_tp_track )
6899 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6900 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
6902 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6904 if ( vtl->current_tp_track )
6905 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6906 vik_layer_emit_update(VIK_LAYER(vtl));
6908 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6910 trw_layer_insert_tp_after_current_tp ( vtl );
6911 vik_layer_emit_update(VIK_LAYER(vtl));
6913 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6914 vik_layer_emit_update(VIK_LAYER(vtl));
6918 * trw_layer_dialog_shift:
6919 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
6921 * Try to reposition a dialog if it's over the specified coord
6922 * so to not obscure the item of interest
6924 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
6926 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
6928 // get parent window position & size
6929 gint win_pos_x, win_pos_y;
6930 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
6932 gint win_size_x, win_size_y;
6933 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
6935 // get own dialog size
6936 gint dia_size_x, dia_size_y;
6937 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
6939 // get own dialog position
6940 gint dia_pos_x, dia_pos_y;
6941 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
6943 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
6944 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
6946 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
6948 gint vp_xx, vp_yy; // In viewport pixels
6949 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
6951 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
6955 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
6957 // Transform Viewport pixels into absolute pixels
6958 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
6959 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
6961 // Is dialog over the point (to within an ^^ edge value)
6962 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
6963 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
6967 gint hh = vik_viewport_get_height ( vvp );
6969 // Consider the difference in viewport to the full window
6970 gint offset_y = dest_y;
6971 // Add difference between dialog and window sizes
6972 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
6974 if ( vp_yy > hh/2 ) {
6975 // Point in bottom half, move window to top half
6976 gtk_window_move ( dialog, dia_pos_x, offset_y );
6979 // Point in top half, move dialog down
6980 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
6984 // Shift left<->right
6985 gint ww = vik_viewport_get_width ( vvp );
6987 // Consider the difference in viewport to the full window
6988 gint offset_x = dest_x;
6989 // Add difference between dialog and window sizes
6990 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
6992 if ( vp_xx > ww/2 ) {
6993 // Point on right, move window to left
6994 gtk_window_move ( dialog, offset_x, dia_pos_y );
6997 // Point on left, move right
6998 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7006 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7010 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7011 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7012 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7013 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7015 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7017 if ( vtl->current_tpl ) {
7018 // get tp pixel position
7019 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7021 // Shift up<->down to try not to obscure the trackpoint.
7022 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7026 if ( vtl->current_tpl )
7027 if ( vtl->current_tp_track )
7028 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7029 /* set layer name and TP data */
7032 /***************************************************************************
7034 ***************************************************************************/
7036 /*** Utility data structures and functions ****/
7040 gint closest_x, closest_y;
7041 gboolean draw_images;
7042 gpointer *closest_wp_id;
7043 VikWaypoint *closest_wp;
7049 gint closest_x, closest_y;
7050 gpointer closest_track_id;
7051 VikTrackpoint *closest_tp;
7057 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7063 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7065 // If waypoint has an image then use the image size to select
7066 if ( params->draw_images && wp->image ) {
7067 gint slackx, slacky;
7068 slackx = wp->image_width / 2;
7069 slacky = wp->image_height / 2;
7071 if ( x <= params->x + slackx && x >= params->x - slackx
7072 && y <= params->y + slacky && y >= params->y - slacky ) {
7073 params->closest_wp_id = id;
7074 params->closest_wp = wp;
7075 params->closest_x = x;
7076 params->closest_y = y;
7079 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7080 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7081 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7083 params->closest_wp_id = id;
7084 params->closest_wp = wp;
7085 params->closest_x = x;
7086 params->closest_y = y;
7090 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7092 GList *tpl = t->trackpoints;
7098 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7104 tp = VIK_TRACKPOINT(tpl->data);
7106 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7108 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7109 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7110 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7112 params->closest_track_id = id;
7113 params->closest_tp = tp;
7114 params->closest_tpl = tpl;
7115 params->closest_x = x;
7116 params->closest_y = y;
7122 // ATM: Leave this as 'Track' only.
7123 // Not overly bothered about having a snap to route trackpoint capability
7124 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7126 TPSearchParams params;
7130 params.closest_track_id = NULL;
7131 params.closest_tp = NULL;
7132 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7133 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7134 return params.closest_tp;
7137 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7139 WPSearchParams params;
7143 params.draw_images = vtl->drawimages;
7144 params.closest_wp = NULL;
7145 params.closest_wp_id = NULL;
7146 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7147 return params.closest_wp;
7151 // Some forward declarations
7152 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7153 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7154 static void marker_end_move ( tool_ed_t *t );
7157 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7161 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7163 // Here always allow snapping back to the original location
7164 // this is useful when one decides not to move the thing afterall
7165 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7168 if ( event->state & GDK_CONTROL_MASK )
7170 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7172 new_coord = tp->coord;
7176 if ( event->state & GDK_SHIFT_MASK )
7178 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7180 new_coord = wp->coord;
7184 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7186 marker_moveto ( t, x, y );
7193 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7195 if ( t->holding && event->button == 1 )
7198 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7201 if ( event->state & GDK_CONTROL_MASK )
7203 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7205 new_coord = tp->coord;
7209 if ( event->state & GDK_SHIFT_MASK )
7211 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7213 new_coord = wp->coord;
7216 marker_end_move ( t );
7218 // Determine if working on a waypoint or a trackpoint
7219 if ( t->is_waypoint ) {
7220 vtl->current_wp->coord = new_coord;
7221 trw_layer_calculate_bounds_waypoints ( vtl );
7224 if ( vtl->current_tpl ) {
7225 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7227 if ( vtl->current_tp_track )
7228 vik_track_calculate_bounds ( vtl->current_tp_track );
7231 if ( vtl->current_tp_track )
7232 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7237 vtl->current_wp = NULL;
7238 vtl->current_wp_id = NULL;
7239 trw_layer_cancel_current_tp ( vtl, FALSE );
7241 vik_layer_emit_update ( VIK_LAYER(vtl) );
7248 Returns true if a waypoint or track is found near the requested event position for this particular layer
7249 The item found is automatically selected
7250 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7252 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7254 if ( event->button != 1 )
7257 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7260 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7264 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7266 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7268 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7269 WPSearchParams wp_params;
7270 wp_params.vvp = vvp;
7271 wp_params.x = event->x;
7272 wp_params.y = event->y;
7273 wp_params.draw_images = vtl->drawimages;
7274 wp_params.closest_wp_id = NULL;
7275 wp_params.closest_wp = NULL;
7277 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7279 if ( wp_params.closest_wp ) {
7282 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7284 // Too easy to move it so must be holding shift to start immediately moving it
7285 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7286 if ( event->state & GDK_SHIFT_MASK ||
7287 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7288 // Put into 'move buffer'
7289 // NB vvp & vw already set in tet
7290 tet->vtl = (gpointer)vtl;
7291 tet->is_waypoint = TRUE;
7293 marker_begin_move (tet, event->x, event->y);
7296 vtl->current_wp = wp_params.closest_wp;
7297 vtl->current_wp_id = wp_params.closest_wp_id;
7299 vik_layer_emit_update ( VIK_LAYER(vtl) );
7305 // Used for both track and route lists
7306 TPSearchParams tp_params;
7307 tp_params.vvp = vvp;
7308 tp_params.x = event->x;
7309 tp_params.y = event->y;
7310 tp_params.closest_track_id = NULL;
7311 tp_params.closest_tp = NULL;
7312 tp_params.bbox = bbox;
7314 if (vtl->tracks_visible) {
7315 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7317 if ( tp_params.closest_tp ) {
7319 // Always select + highlight the track
7320 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7322 tet->is_waypoint = FALSE;
7324 // Select the Trackpoint
7325 // Can move it immediately when control held or it's the previously selected tp
7326 if ( event->state & GDK_CONTROL_MASK ||
7327 vtl->current_tpl == tp_params.closest_tpl ) {
7328 // Put into 'move buffer'
7329 // NB vvp & vw already set in tet
7330 tet->vtl = (gpointer)vtl;
7331 marker_begin_move (tet, event->x, event->y);
7334 vtl->current_tpl = tp_params.closest_tpl;
7335 vtl->current_tp_id = tp_params.closest_track_id;
7336 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7338 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7341 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7343 vik_layer_emit_update ( VIK_LAYER(vtl) );
7348 // Try again for routes
7349 if (vtl->routes_visible) {
7350 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7352 if ( tp_params.closest_tp ) {
7354 // Always select + highlight the track
7355 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7357 tet->is_waypoint = FALSE;
7359 // Select the Trackpoint
7360 // Can move it immediately when control held or it's the previously selected tp
7361 if ( event->state & GDK_CONTROL_MASK ||
7362 vtl->current_tpl == tp_params.closest_tpl ) {
7363 // Put into 'move buffer'
7364 // NB vvp & vw already set in tet
7365 tet->vtl = (gpointer)vtl;
7366 marker_begin_move (tet, event->x, event->y);
7369 vtl->current_tpl = tp_params.closest_tpl;
7370 vtl->current_tp_id = tp_params.closest_track_id;
7371 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7373 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7376 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7378 vik_layer_emit_update ( VIK_LAYER(vtl) );
7383 /* these aren't the droids you're looking for */
7384 vtl->current_wp = NULL;
7385 vtl->current_wp_id = NULL;
7386 trw_layer_cancel_current_tp ( vtl, FALSE );
7389 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7394 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7396 if ( event->button != 3 )
7399 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7402 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7405 /* Post menu for the currently selected item */
7407 /* See if a track is selected */
7408 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7409 if ( track && track->visible ) {
7411 if ( track->name ) {
7413 if ( vtl->track_right_click_menu )
7414 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
7416 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7423 if ( track->is_route )
7424 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7426 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7428 if ( trkf && udataU.uuid ) {
7431 if ( track->is_route )
7432 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7434 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7436 trw_layer_sublayer_add_menu_items ( vtl,
7437 vtl->track_right_click_menu,
7439 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7445 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7451 /* See if a waypoint is selected */
7452 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7453 if ( waypoint && waypoint->visible ) {
7454 if ( waypoint->name ) {
7456 if ( vtl->wp_right_click_menu )
7457 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7459 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7462 udata.wp = waypoint;
7465 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7467 if ( wpf && udata.uuid ) {
7468 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7470 trw_layer_sublayer_add_menu_items ( vtl,
7471 vtl->wp_right_click_menu,
7473 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7478 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7487 /* background drawing hook, to be passed the viewport */
7488 static gboolean tool_sync_done = TRUE;
7490 static gboolean tool_sync(gpointer data)
7492 VikViewport *vvp = data;
7493 gdk_threads_enter();
7494 vik_viewport_sync(vvp);
7495 tool_sync_done = TRUE;
7496 gdk_threads_leave();
7500 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7503 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7504 gdk_gc_set_function ( t->gc, GDK_INVERT );
7505 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7506 vik_viewport_sync(t->vvp);
7511 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7513 VikViewport *vvp = t->vvp;
7514 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7515 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7519 if (tool_sync_done) {
7520 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7521 tool_sync_done = FALSE;
7525 static void marker_end_move ( tool_ed_t *t )
7527 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7528 g_object_unref ( t->gc );
7532 /*** Edit waypoint ****/
7534 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7536 tool_ed_t *t = g_new(tool_ed_t, 1);
7542 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7544 WPSearchParams params;
7545 tool_ed_t *t = data;
7546 VikViewport *vvp = t->vvp;
7548 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7555 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7558 if ( vtl->current_wp && vtl->current_wp->visible )
7560 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7562 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7564 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7565 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7567 if ( event->button == 3 )
7568 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7570 marker_begin_move(t, event->x, event->y);
7577 params.x = event->x;
7578 params.y = event->y;
7579 params.draw_images = vtl->drawimages;
7580 params.closest_wp_id = NULL;
7581 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7582 params.closest_wp = NULL;
7583 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7584 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7586 // how do we get here?
7587 marker_begin_move(t, event->x, event->y);
7588 g_critical("shouldn't be here");
7591 else if ( params.closest_wp )
7593 if ( event->button == 3 )
7594 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7596 vtl->waypoint_rightclick = FALSE;
7598 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7600 vtl->current_wp = params.closest_wp;
7601 vtl->current_wp_id = params.closest_wp_id;
7603 /* could make it so don't update if old WP is off screen and new is null but oh well */
7604 vik_layer_emit_update ( VIK_LAYER(vtl) );
7608 vtl->current_wp = NULL;
7609 vtl->current_wp_id = NULL;
7610 vtl->waypoint_rightclick = FALSE;
7611 vik_layer_emit_update ( VIK_LAYER(vtl) );
7615 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7617 tool_ed_t *t = data;
7618 VikViewport *vvp = t->vvp;
7620 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7625 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7628 if ( event->state & GDK_CONTROL_MASK )
7630 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7632 new_coord = tp->coord;
7636 if ( event->state & GDK_SHIFT_MASK )
7638 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7639 if ( wp && wp != vtl->current_wp )
7640 new_coord = wp->coord;
7645 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7647 marker_moveto ( t, x, y );
7654 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7656 tool_ed_t *t = data;
7657 VikViewport *vvp = t->vvp;
7659 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7662 if ( t->holding && event->button == 1 )
7665 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7668 if ( event->state & GDK_CONTROL_MASK )
7670 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7672 new_coord = tp->coord;
7676 if ( event->state & GDK_SHIFT_MASK )
7678 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7679 if ( wp && wp != vtl->current_wp )
7680 new_coord = wp->coord;
7683 marker_end_move ( t );
7685 vtl->current_wp->coord = new_coord;
7687 trw_layer_calculate_bounds_waypoints ( vtl );
7688 vik_layer_emit_update ( VIK_LAYER(vtl) );
7691 /* PUT IN RIGHT PLACE!!! */
7692 if ( event->button == 3 && vtl->waypoint_rightclick )
7694 if ( vtl->wp_right_click_menu )
7695 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7696 if ( vtl->current_wp ) {
7697 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7698 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 );
7699 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7701 vtl->waypoint_rightclick = FALSE;
7706 /*** New track ****/
7708 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7715 GdkDrawable *drawable;
7721 * Draw specified pixmap
7723 static gboolean draw_sync ( gpointer data )
7725 draw_sync_t *ds = (draw_sync_t*) data;
7726 // Sometimes don't want to draw
7727 // normally because another update has taken precedent such as panning the display
7728 // which means this pixmap is no longer valid
7729 if ( ds->vtl->draw_sync_do ) {
7730 gdk_threads_enter();
7731 gdk_draw_drawable (ds->drawable,
7734 0, 0, 0, 0, -1, -1);
7735 ds->vtl->draw_sync_done = TRUE;
7736 gdk_threads_leave();
7741 static gchar* distance_string (gdouble distance)
7745 /* draw label with distance */
7746 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7747 switch (dist_units) {
7748 case VIK_UNITS_DISTANCE_MILES:
7749 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7750 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7751 } else if (distance < 1609.4) {
7752 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7754 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7758 // VIK_UNITS_DISTANCE_KILOMETRES
7759 if (distance >= 1000 && distance < 100000) {
7760 g_sprintf(str, "%3.2f km", distance/1000.0);
7761 } else if (distance < 1000) {
7762 g_sprintf(str, "%d m", (int)distance);
7764 g_sprintf(str, "%d km", (int)distance/1000);
7768 return g_strdup (str);
7772 * Actually set the message in statusbar
7774 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
7776 // Only show elevation data when track has some elevation properties
7777 gchar str_gain_loss[64];
7778 str_gain_loss[0] = '\0';
7779 gchar str_last_step[64];
7780 str_last_step[0] = '\0';
7781 gchar *str_total = distance_string (distance);
7783 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7784 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7785 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7787 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7790 if ( last_step > 0 ) {
7791 gchar *tmp = distance_string (last_step);
7792 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
7796 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
7798 // Write with full gain/loss information
7799 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
7800 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
7802 g_free ( str_total );
7806 * Figure out what information should be set in the statusbar and then write it
7808 static void update_statusbar ( VikTrwLayer *vtl )
7810 // Get elevation data
7811 gdouble elev_gain, elev_loss;
7812 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7814 /* Find out actual distance of current track */
7815 gdouble distance = vik_track_get_length (vtl->current_track);
7817 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
7821 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7823 /* if we haven't sync'ed yet, we don't have time to do more. */
7824 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7825 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7826 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7828 static GdkPixmap *pixmap = NULL;
7830 // Need to check in case window has been resized
7831 w1 = vik_viewport_get_width(vvp);
7832 h1 = vik_viewport_get_height(vvp);
7834 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
7836 gdk_drawable_get_size (pixmap, &w2, &h2);
7837 if (w1 != w2 || h1 != h2) {
7838 g_object_unref ( G_OBJECT ( pixmap ) );
7839 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
7842 // Reset to background
7843 gdk_draw_drawable (pixmap,
7844 vtl->current_track_newpoint_gc,
7845 vik_viewport_get_pixmap(vvp),
7846 0, 0, 0, 0, -1, -1);
7848 draw_sync_t *passalong;
7851 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7853 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7854 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7855 // thus when we come to reset to the background it would include what we have already drawn!!
7856 gdk_draw_line ( pixmap,
7857 vtl->current_track_newpoint_gc,
7858 x1, y1, event->x, event->y );
7859 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7861 /* Find out actual distance of current track */
7862 gdouble distance = vik_track_get_length (vtl->current_track);
7864 // Now add distance to where the pointer is //
7867 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7868 vik_coord_to_latlon ( &coord, &ll );
7869 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
7870 distance = distance + last_step;
7872 // Get elevation data
7873 gdouble elev_gain, elev_loss;
7874 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7876 // Adjust elevation data (if available) for the current pointer position
7878 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7879 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7880 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7881 // Adjust elevation of last track point
7882 if ( elev_new > last_tpt->altitude )
7884 elev_gain += elev_new - last_tpt->altitude;
7887 elev_loss += last_tpt->altitude - elev_new;
7892 // Display of the distance 'tooltip' during track creation is controlled by a preference
7894 if ( a_vik_get_create_track_tooltip() ) {
7896 gchar *str = distance_string (distance);
7898 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7899 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
7900 pango_layout_set_text (pl, str, -1);
7902 pango_layout_get_pixel_size ( pl, &wd, &hd );
7905 // offset from cursor a bit depending on font size
7909 // Create a background block to make the text easier to read over the background map
7910 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7911 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7912 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7914 g_object_unref ( G_OBJECT ( pl ) );
7915 g_object_unref ( G_OBJECT ( background_block_gc ) );
7919 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7920 passalong->vtl = vtl;
7921 passalong->pixmap = pixmap;
7922 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
7923 passalong->gc = vtl->current_track_newpoint_gc;
7927 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
7929 // Update statusbar with full gain/loss information
7930 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7932 // draw pixmap when we have time to
7933 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7934 vtl->draw_sync_done = FALSE;
7935 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7937 return VIK_LAYER_TOOL_ACK;
7940 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7942 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7943 vtl->current_track = NULL;
7944 vik_layer_emit_update ( VIK_LAYER(vtl) );
7946 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7948 if ( vtl->current_track->trackpoints )
7950 GList *last = g_list_last(vtl->current_track->trackpoints);
7951 g_free ( last->data );
7952 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7955 update_statusbar ( vtl );
7957 vik_layer_emit_update ( VIK_LAYER(vtl) );
7964 * Common function to handle trackpoint button requests on either a route or a track
7965 * . enables adding a point via normal click
7966 * . enables removal of last point via right click
7967 * . finishing of the track or route via double clicking
7969 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7973 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7976 if ( event->button == 2 ) {
7977 // As the display is panning, the new track pixmap is now invalid so don't draw it
7978 // otherwise this drawing done results in flickering back to an old image
7979 vtl->draw_sync_do = FALSE;
7983 if ( event->button == 3 )
7985 if ( !vtl->current_track )
7988 if ( vtl->current_track->trackpoints )
7990 GList *last = g_list_last(vtl->current_track->trackpoints);
7991 g_free ( last->data );
7992 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7994 vik_track_calculate_bounds ( vtl->current_track );
7995 update_statusbar ( vtl );
7997 vik_layer_emit_update ( VIK_LAYER(vtl) );
8001 if ( event->type == GDK_2BUTTON_PRESS )
8003 /* subtract last (duplicate from double click) tp then end */
8004 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8006 GList *last = g_list_last(vtl->current_track->trackpoints);
8007 g_free ( last->data );
8008 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8009 /* undo last, then end */
8010 vtl->current_track = NULL;
8012 vik_layer_emit_update ( VIK_LAYER(vtl) );
8016 tp = vik_trackpoint_new();
8017 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8019 /* snap to other TP */
8020 if ( event->state & GDK_CONTROL_MASK )
8022 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8024 tp->coord = other_tp->coord;
8027 tp->newsegment = FALSE;
8028 tp->has_timestamp = FALSE;
8031 if ( vtl->current_track ) {
8032 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
8033 /* Auto attempt to get elevation from DEM data (if it's available) */
8034 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8035 vik_track_calculate_bounds ( vtl->current_track );
8038 vtl->ct_x1 = vtl->ct_x2;
8039 vtl->ct_y1 = vtl->ct_y2;
8040 vtl->ct_x2 = event->x;
8041 vtl->ct_y2 = event->y;
8043 vik_layer_emit_update ( VIK_LAYER(vtl) );
8047 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8049 // ----------------------------------------------------- if current is a route - switch to new track
8050 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8052 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8053 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8055 new_track_create_common ( vtl, name );
8060 return tool_new_track_or_route_click ( vtl, event, vvp );
8063 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8065 if ( event->button == 2 ) {
8066 // Pan moving ended - enable potential point drawing again
8067 vtl->draw_sync_do = TRUE;
8068 vtl->draw_sync_done = TRUE;
8072 /*** New route ****/
8074 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8079 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8081 // -------------------------- if current is a track - switch to new route
8082 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8084 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8085 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
8086 new_route_create_common ( vtl, name );
8090 return tool_new_track_or_route_click ( vtl, event, vvp );
8093 /*** New waypoint ****/
8095 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8100 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8103 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8105 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8106 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8107 trw_layer_calculate_bounds_waypoints ( vtl );
8108 vik_layer_emit_update ( VIK_LAYER(vtl) );
8114 /*** Edit trackpoint ****/
8116 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8118 tool_ed_t *t = g_new(tool_ed_t, 1);
8124 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8126 tool_ed_t *t = data;
8127 VikViewport *vvp = t->vvp;
8128 TPSearchParams params;
8129 /* OUTDATED DOCUMENTATION:
8130 find 5 pixel range on each side. then put these UTM, and a pointer
8131 to the winning track name (and maybe the winning track itself), and a
8132 pointer to the winning trackpoint, inside an array or struct. pass
8133 this along, do a foreach on the tracks which will do a foreach on the
8136 params.x = event->x;
8137 params.y = event->y;
8138 params.closest_track_id = NULL;
8139 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8140 params.closest_tp = NULL;
8141 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8143 if ( event->button != 1 )
8146 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8149 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8152 if ( vtl->current_tpl )
8154 /* 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.) */
8155 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8156 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8161 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8163 if ( current_tr->visible &&
8164 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8165 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8166 marker_begin_move ( t, event->x, event->y );
8172 if ( vtl->tracks_visible )
8173 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8175 if ( params.closest_tp )
8177 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8178 vtl->current_tpl = params.closest_tpl;
8179 vtl->current_tp_id = params.closest_track_id;
8180 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8181 trw_layer_tpwin_init ( vtl );
8182 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8183 vik_layer_emit_update ( VIK_LAYER(vtl) );
8187 if ( vtl->routes_visible )
8188 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8190 if ( params.closest_tp )
8192 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8193 vtl->current_tpl = params.closest_tpl;
8194 vtl->current_tp_id = params.closest_track_id;
8195 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8196 trw_layer_tpwin_init ( vtl );
8197 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8198 vik_layer_emit_update ( VIK_LAYER(vtl) );
8202 /* these aren't the droids you're looking for */
8206 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8208 tool_ed_t *t = data;
8209 VikViewport *vvp = t->vvp;
8211 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8217 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8220 if ( event->state & GDK_CONTROL_MASK )
8222 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8223 if ( tp && tp != vtl->current_tpl->data )
8224 new_coord = tp->coord;
8226 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8229 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8230 marker_moveto ( t, x, y );
8238 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8240 tool_ed_t *t = data;
8241 VikViewport *vvp = t->vvp;
8243 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8245 if ( event->button != 1)
8250 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8253 if ( event->state & GDK_CONTROL_MASK )
8255 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8256 if ( tp && tp != vtl->current_tpl->data )
8257 new_coord = tp->coord;
8260 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8261 if ( vtl->current_tp_track )
8262 vik_track_calculate_bounds ( vtl->current_tp_track );
8264 marker_end_move ( t );
8266 /* diff dist is diff from orig */
8268 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8270 vik_layer_emit_update ( VIK_LAYER(vtl) );
8277 /*** Route Finder ***/
8278 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8283 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8286 if ( !vtl ) return FALSE;
8287 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8288 if ( event->button == 3 && vtl->route_finder_current_track ) {
8290 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8292 vtl->route_finder_coord = *new_end;
8294 vik_layer_emit_update ( VIK_LAYER(vtl) );
8295 /* remove last ' to:...' */
8296 if ( vtl->route_finder_current_track->comment ) {
8297 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8298 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8299 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8300 last_to - vtl->route_finder_current_track->comment - 1);
8301 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8306 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8307 struct LatLon start, end;
8309 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8310 vik_coord_to_latlon ( &(tmp), &end );
8311 vtl->route_finder_coord = tmp; /* for continuations */
8313 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8314 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8315 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8317 vtl->route_finder_check_added_track = TRUE;
8318 vtl->route_finder_started = FALSE;
8321 vik_routing_default_find ( vtl, start, end);
8323 /* see if anything was done -- a track was added or appended to */
8324 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8325 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 ) );
8326 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8327 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8328 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8329 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8332 if ( vtl->route_finder_added_track )
8333 vik_track_calculate_bounds ( vtl->route_finder_added_track );
8335 vtl->route_finder_added_track = NULL;
8336 vtl->route_finder_check_added_track = FALSE;
8337 vtl->route_finder_append = FALSE;
8339 vik_layer_emit_update ( VIK_LAYER(vtl) );
8341 vtl->route_finder_started = TRUE;
8342 vtl->route_finder_coord = tmp;
8343 vtl->route_finder_current_track = NULL;
8348 /*** Show picture ****/
8350 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8355 /* Params are: vvp, event, last match found or NULL */
8356 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8358 if ( wp->image && wp->visible )
8360 gint x, y, slackx, slacky;
8361 GdkEventButton *event = (GdkEventButton *) params[1];
8363 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8364 slackx = wp->image_width / 2;
8365 slacky = wp->image_height / 2;
8366 if ( x <= event->x + slackx && x >= event->x - slackx
8367 && y <= event->y + slacky && y >= event->y - slacky )
8369 params[2] = wp->image; /* we've found a match. however continue searching
8370 * since we want to find the last match -- that
8371 * is, the match that was drawn last. */
8376 static void trw_layer_show_picture ( gpointer pass_along[6] )
8378 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8380 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8383 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8384 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8385 g_free ( quoted_file );
8386 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8388 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() );
8389 g_error_free ( err );
8392 #endif /* WINDOWS */
8395 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8397 gpointer params[3] = { vvp, event, NULL };
8398 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8400 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8403 static gpointer pass_along[6];
8404 pass_along[0] = vtl;
8405 pass_along[5] = params[2];
8406 trw_layer_show_picture ( pass_along );
8407 return TRUE; /* found a match */
8410 return FALSE; /* go through other layers, searching for a match */
8413 /***************************************************************************
8415 ***************************************************************************/
8418 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8420 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8421 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8424 /* Structure for thumbnail creating data used in the background thread */
8426 VikTrwLayer *vtl; // Layer needed for redrawing
8427 GSList *pics; // Image list
8428 } thumbnail_create_thread_data;
8430 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8432 guint total = g_slist_length(tctd->pics), done = 0;
8433 while ( tctd->pics )
8435 a_thumbnails_create ( (gchar *) tctd->pics->data );
8436 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8438 return -1; /* Abort thread */
8440 tctd->pics = tctd->pics->next;
8443 // Redraw to show the thumbnails as they are now created
8444 if ( IS_VIK_LAYER(tctd->vtl) )
8445 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8450 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8452 while ( tctd->pics )
8454 g_free ( tctd->pics->data );
8455 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8460 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8462 if ( ! vtl->has_verified_thumbnails )
8464 GSList *pics = NULL;
8465 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8468 gint len = g_slist_length ( pics );
8469 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8470 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8473 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8475 (vik_thr_func) create_thumbnails_thread,
8477 (vik_thr_free_func) thumbnail_create_thread_free,
8485 static const gchar* my_track_colors ( gint ii )
8487 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8499 // Fast and reliable way of returning a colour
8500 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8503 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8505 GHashTableIter iter;
8506 gpointer key, value;
8510 g_hash_table_iter_init ( &iter, vtl->tracks );
8512 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8514 // Tracks get a random spread of colours if not already assigned
8515 if ( ! VIK_TRACK(value)->has_color ) {
8516 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8517 VIK_TRACK(value)->color = vtl->track_color;
8519 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8521 VIK_TRACK(value)->has_color = TRUE;
8524 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8527 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8533 g_hash_table_iter_init ( &iter, vtl->routes );
8535 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8537 // Routes get an intermix of reds
8538 if ( ! VIK_TRACK(value)->has_color ) {
8540 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8542 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8543 VIK_TRACK(value)->has_color = TRUE;
8546 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8553 * (Re)Calculate the bounds of the waypoints in this layer,
8554 * This should be called whenever waypoints are changed
8556 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
8558 struct LatLon topleft = { 0.0, 0.0 };
8559 struct LatLon bottomright = { 0.0, 0.0 };
8562 GHashTableIter iter;
8563 gpointer key, value;
8565 g_hash_table_iter_init ( &iter, vtl->waypoints );
8567 // Set bounds to first point
8568 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
8569 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
8570 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
8573 // Ensure there is another point...
8574 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
8576 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8578 // See if this point increases the bounds.
8579 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
8581 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
8582 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
8583 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
8584 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
8588 vtl->waypoints_bbox.north = topleft.lat;
8589 vtl->waypoints_bbox.east = bottomright.lon;
8590 vtl->waypoints_bbox.south = bottomright.lat;
8591 vtl->waypoints_bbox.west = topleft.lon;
8594 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
8596 vik_track_calculate_bounds ( trk );
8599 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
8601 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8602 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8605 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
8607 trw_layer_verify_thumbnails ( vtl, vvp );
8608 trw_layer_track_alloc_colors ( vtl );
8610 trw_layer_calculate_bounds_waypoints ( vtl );
8611 trw_layer_calculate_bounds_tracks ( vtl );
8614 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8616 return vtl->coord_mode;
8620 * Uniquify the whole layer
8621 * Also requires the layers panel as the names shown there need updating too
8622 * Returns whether the operation was successful or not
8624 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8627 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8628 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8629 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8635 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8637 vik_coord_convert ( &(wp->coord), *dest_mode );
8640 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
8642 vik_track_convert ( tr, *dest_mode );
8645 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8647 if ( vtl->coord_mode != dest_mode )
8649 vtl->coord_mode = dest_mode;
8650 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8651 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8652 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
8656 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8658 vtl->menu_selection = selection;
8661 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8663 return (vtl->menu_selection);
8666 /* ----------- Downloading maps along tracks --------------- */
8668 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8670 /* TODO: calculating based on current size of viewport */
8671 const gdouble w_at_zoom_0_125 = 0.0013;
8672 const gdouble h_at_zoom_0_125 = 0.0011;
8673 gdouble zoom_factor = zoom_level/0.125;
8675 wh->lat = h_at_zoom_0_125 * zoom_factor;
8676 wh->lon = w_at_zoom_0_125 * zoom_factor;
8678 return 0; /* all OK */
8681 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8683 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8684 (dist->lat >= ABS(to->north_south - from->north_south)))
8687 VikCoord *coord = g_malloc(sizeof(VikCoord));
8688 coord->mode = VIK_COORD_LATLON;
8690 if (ABS(gradient) < 1) {
8691 if (from->east_west > to->east_west)
8692 coord->east_west = from->east_west - dist->lon;
8694 coord->east_west = from->east_west + dist->lon;
8695 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8697 if (from->north_south > to->north_south)
8698 coord->north_south = from->north_south - dist->lat;
8700 coord->north_south = from->north_south + dist->lat;
8701 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8707 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8709 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8710 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8712 VikCoord *next = from;
8714 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8716 list = g_list_prepend(list, next);
8722 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8724 typedef struct _Rect {
8729 #define GLRECT(iter) ((Rect *)((iter)->data))
8732 GList *rects_to_download = NULL;
8735 if (get_download_area_width(vvp, zoom_level, &wh))
8738 GList *iter = tr->trackpoints;
8742 gboolean new_map = TRUE;
8743 VikCoord *cur_coord, tl, br;
8746 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8748 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8749 rect = g_malloc(sizeof(Rect));
8752 rect->center = *cur_coord;
8753 rects_to_download = g_list_prepend(rects_to_download, rect);
8758 gboolean found = FALSE;
8759 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8760 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8771 GList *fillins = NULL;
8772 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8773 /* seems that ATM the function get_next_coord works only for LATLON */
8774 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8775 /* fill-ins for far apart points */
8776 GList *cur_rect, *next_rect;
8777 for (cur_rect = rects_to_download;
8778 (next_rect = cur_rect->next) != NULL;
8779 cur_rect = cur_rect->next) {
8780 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8781 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8782 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8786 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8789 GList *iter = fillins;
8791 cur_coord = (VikCoord *)(iter->data);
8792 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8793 rect = g_malloc(sizeof(Rect));
8796 rect->center = *cur_coord;
8797 rects_to_download = g_list_prepend(rects_to_download, rect);
8802 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8803 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8807 for (iter = fillins; iter; iter = iter->next)
8809 g_list_free(fillins);
8811 if (rects_to_download) {
8812 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8813 g_free(rect_iter->data);
8814 g_list_free(rects_to_download);
8818 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8821 gint selected_map, default_map;
8822 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8823 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8824 gint selected_zoom, default_zoom;
8828 VikTrwLayer *vtl = pass_along[0];
8829 VikLayersPanel *vlp = pass_along[1];
8831 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8832 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8834 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8838 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8840 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8841 int num_maps = g_list_length(vmls);
8844 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8848 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8849 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8851 gchar **np = map_names;
8852 VikMapsLayer **lp = map_layers;
8853 for (i = 0; i < num_maps; i++) {
8854 gboolean dup = FALSE;
8855 vml = (VikMapsLayer *)(vmls->data);
8856 for (j = 0; j < i; j++) { /* no duplicate allowed */
8857 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8864 *np++ = vik_maps_layer_get_map_label(vml);
8870 num_maps = lp - map_layers;
8872 for (default_map = 0; default_map < num_maps; default_map++) {
8873 /* TODO: check for parent layer's visibility */
8874 if (VIK_LAYER(map_layers[default_map])->visible)
8877 default_map = (default_map == num_maps) ? 0 : default_map;
8879 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8880 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8881 if (cur_zoom == zoom_vals[default_zoom])
8884 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8886 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8889 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8892 for (i = 0; i < num_maps; i++)
8893 g_free(map_names[i]);
8901 /**** lowest waypoint number calculation ***/
8902 static gint highest_wp_number_name_to_number(const gchar *name) {
8903 if ( strlen(name) == 3 ) {
8905 if ( n < 100 && name[0] != '0' )
8907 if ( n < 10 && name[0] != '0' )
8915 static void highest_wp_number_reset(VikTrwLayer *vtl)
8917 vtl->highest_wp_number = -1;
8920 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8922 /* if is bigger that top, add it */
8923 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8924 if ( new_wp_num > vtl->highest_wp_number )
8925 vtl->highest_wp_number = new_wp_num;
8928 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8930 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8931 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8932 if ( vtl->highest_wp_number == old_wp_num ) {
8934 vtl->highest_wp_number--;
8936 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8937 /* search down until we find something that *does* exist */
8939 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8940 vtl->highest_wp_number--;
8941 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8946 /* get lowest unused number */
8947 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8950 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8952 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8953 return g_strdup(buf);