2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
26 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
33 #include "vikmapslayer.h"
34 #include "vikgpslayer.h"
35 #include "viktrwlayer_tpwin.h"
36 #include "viktrwlayer_propwin.h"
37 #ifdef VIK_CONFIG_GEOTAG
38 #include "viktrwlayer_geotag.h"
39 #include "geotag_exif.h"
41 #include "garminsymbols.h"
42 #include "thumbnails.h"
43 #include "background.h"
48 #include "geonamessearch.h"
49 #ifdef VIK_CONFIG_OPENSTREETMAP
50 #include "osm-traces.h"
53 #include "datasources.h"
54 #include "datasource_gps.h"
57 #include "icons/icons.h"
71 #include <gdk/gdkkeysyms.h>
73 #include <glib/gstdio.h>
74 #include <glib/gi18n.h>
76 #ifdef VIK_CONFIG_GOOGLE
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
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 #ifdef VIK_CONFIG_GOOGLE
359 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
360 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
363 static void cached_pixbuf_free ( CachedPixbuf *cp );
364 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
366 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
367 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
369 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
370 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
372 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
373 static void highest_wp_number_reset(VikTrwLayer *vtl);
374 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
375 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
377 // Note for the following tool GtkRadioActionEntry texts:
378 // the very first text value is an internal name not displayed anywhere
379 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
380 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
381 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
382 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
383 static VikToolInterface trw_layer_tools[] = {
384 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
385 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
386 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
388 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
390 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
391 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
392 (VikToolMouseFunc) tool_new_track_click,
393 (VikToolMouseMoveFunc) tool_new_track_move,
394 (VikToolMouseFunc) tool_new_track_release,
395 (VikToolKeyFunc) tool_new_track_key_press,
396 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
397 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
399 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
400 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
401 (VikToolMouseFunc) tool_new_route_click,
402 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
403 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
404 (VikToolKeyFunc) tool_new_track_key_press, // -/#
405 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
406 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
408 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
409 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
410 (VikToolMouseFunc) tool_edit_waypoint_click,
411 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
412 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
414 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
416 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
417 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
418 (VikToolMouseFunc) tool_edit_trackpoint_click,
419 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
420 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
422 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
424 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
425 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
426 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
428 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
430 #ifdef VIK_CONFIG_GOOGLE
431 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
432 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
433 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
435 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
440 TOOL_CREATE_WAYPOINT=0,
444 TOOL_EDIT_TRACKPOINT,
446 #ifdef VIK_CONFIG_GOOGLE
452 /****** PARAMETERS ******/
454 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
455 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
457 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
458 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
460 #define MIN_POINT_SIZE 2
461 #define MAX_POINT_SIZE 10
463 #define MIN_ARROW_SIZE 3
464 #define MAX_ARROW_SIZE 20
466 static VikLayerParamScale params_scales[] = {
467 /* min max step digits */
468 { 1, 10, 1, 0 }, /* line_thickness */
469 { 0, 100, 1, 0 }, /* track draw speed factor */
470 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
471 /* 5 * step == how much to turn */
472 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
473 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
474 { 5, 500, 5, 0 }, // 5: image cache_size - " "
475 { 0, 8, 1, 0 }, // 6: Background line thickness
476 { 1, 64, 1, 0 }, /* wpsize */
477 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
478 { 1, 100, 1, 0 }, // 9: elevation factor
479 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
480 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
483 static gchar* params_font_sizes[] = {
484 N_("Extra Extra Small"),
490 N_("Extra Extra Large"),
493 static VikLayerParamData black_color_default ( void ) {
494 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
496 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
497 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
498 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
499 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
500 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
501 static VikLayerParamData trackbgcolor_default ( void ) {
502 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
504 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
505 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
506 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
508 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
509 static VikLayerParamData wptextcolor_default ( void ) {
510 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
512 static VikLayerParamData wpbgcolor_default ( void ) {
513 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
515 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
516 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
518 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
519 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
520 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
522 VikLayerParam trw_layer_params[] = {
523 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
524 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
525 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default },
527 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default },
528 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
529 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default },
530 { 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 },
531 { 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 },
532 { 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 },
533 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL, trkdirectionsize_default },
534 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default },
535 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL, trkpointsize_default },
536 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default },
537 { 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 },
539 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
540 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 },
541 { 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 },
543 { 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 },
544 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default },
545 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
546 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default },
548 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default },
549 { 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 },
550 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default },
551 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default },
552 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default },
553 { 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 },
554 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default },
555 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[7], NULL, NULL, wpsize_default },
556 { 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 },
558 { 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 },
559 { 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 },
560 { 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 },
561 { 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 },
564 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
566 // Sublayer visibilities
605 *** 1) Add to trw_layer_params and enumeration
606 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
609 /****** END PARAMETERS ******/
611 /* Layer Interface function definitions */
612 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
613 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
614 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
615 static void trw_layer_free ( VikTrwLayer *trwlayer );
616 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
617 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
618 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
619 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
620 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
621 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
622 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
623 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
624 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
625 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
626 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
627 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
628 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
629 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
630 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
631 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
632 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
633 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
634 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
635 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
636 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
637 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
638 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
639 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
640 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
641 /* End Layer Interface function definitions */
643 VikLayerInterface vik_trw_layer_interface = {
650 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
654 params_groups, /* params_groups */
655 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
659 (VikLayerFuncCreate) trw_layer_create,
660 (VikLayerFuncRealize) trw_layer_realize,
661 (VikLayerFuncPostRead) trw_layer_post_read,
662 (VikLayerFuncFree) trw_layer_free,
664 (VikLayerFuncProperties) NULL,
665 (VikLayerFuncDraw) trw_layer_draw,
666 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
668 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
669 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
671 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
672 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
674 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
675 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
676 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
677 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
678 (VikLayerFuncLayerSelected) trw_layer_selected,
680 (VikLayerFuncMarshall) trw_layer_marshall,
681 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
683 (VikLayerFuncSetParam) trw_layer_set_param,
684 (VikLayerFuncGetParam) trw_layer_get_param,
686 (VikLayerFuncReadFileData) a_gpspoint_read_file,
687 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
689 (VikLayerFuncDeleteItem) trw_layer_del_item,
690 (VikLayerFuncCutItem) trw_layer_cut_item,
691 (VikLayerFuncCopyItem) trw_layer_copy_item,
692 (VikLayerFuncPasteItem) trw_layer_paste_item,
693 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
695 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
697 (VikLayerFuncSelectClick) trw_layer_select_click,
698 (VikLayerFuncSelectMove) trw_layer_select_move,
699 (VikLayerFuncSelectRelease) trw_layer_select_release,
700 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
703 GType vik_trw_layer_get_type ()
705 static GType vtl_type = 0;
709 static const GTypeInfo vtl_info =
711 sizeof (VikTrwLayerClass),
712 NULL, /* base_init */
713 NULL, /* base_finalize */
714 NULL, /* class init */
715 NULL, /* class_finalize */
716 NULL, /* class_data */
717 sizeof (VikTrwLayer),
719 NULL /* instance init */
721 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
727 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
729 static gpointer pass_along[6];
735 pass_along[1] = NULL;
736 pass_along[2] = GINT_TO_POINTER (subtype);
737 pass_along[3] = sublayer;
738 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
739 pass_along[5] = NULL;
741 trw_layer_delete_item ( pass_along );
744 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
746 static gpointer pass_along[6];
752 pass_along[1] = NULL;
753 pass_along[2] = GINT_TO_POINTER (subtype);
754 pass_along[3] = sublayer;
755 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
756 pass_along[5] = NULL;
758 trw_layer_copy_item_cb(pass_along);
759 trw_layer_cut_item_cb(pass_along);
762 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
764 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
765 gint subtype = GPOINTER_TO_INT (pass_along[2]);
766 gpointer * sublayer = pass_along[3];
770 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
774 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
775 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
776 if ( wp && wp->name )
779 name = NULL; // Broken :(
781 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
782 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
783 if ( trk && trk->name )
786 name = NULL; // Broken :(
789 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
790 if ( trk && trk->name )
793 name = NULL; // Broken :(
796 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
797 subtype, len, name, data);
801 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
803 trw_layer_copy_item_cb(pass_along);
804 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
805 trw_layer_delete_item(pass_along);
808 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
810 // Slightly cheating method, routing via the panels capability
811 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
814 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
824 GByteArray *ba = g_byte_array_new ();
826 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
827 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
828 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
829 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
831 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
834 g_byte_array_append ( ba, id, il );
842 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
849 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
853 w = vik_waypoint_unmarshall ( item, len );
854 // When copying - we'll create a new name based on the original
855 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
856 vik_trw_layer_add_waypoint ( vtl, name, w );
857 waypoint_convert (NULL, w, &vtl->coord_mode);
859 trw_layer_calculate_bounds_waypoints ( vtl );
861 // Consider if redraw necessary for the new item
862 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
863 vik_layer_emit_update ( VIK_LAYER(vtl) );
866 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
870 t = vik_track_unmarshall ( item, len );
871 // When copying - we'll create a new name based on the original
872 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
873 vik_trw_layer_add_track ( vtl, name, t );
874 vik_track_convert (t, vtl->coord_mode);
876 // Consider if redraw necessary for the new item
877 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
878 vik_layer_emit_update ( VIK_LAYER(vtl) );
881 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
885 t = vik_track_unmarshall ( item, len );
886 // When copying - we'll create a new name based on the original
887 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
888 vik_trw_layer_add_route ( vtl, name, t );
889 vik_track_convert (t, vtl->coord_mode);
891 // Consider if redraw necessary for the new item
892 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
893 vik_layer_emit_update ( VIK_LAYER(vtl) );
899 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
906 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
910 case PARAM_TV: vtl->tracks_visible = data.b; break;
911 case PARAM_WV: vtl->waypoints_visible = data.b; break;
912 case PARAM_RV: vtl->routes_visible = data.b; break;
913 case PARAM_DM: vtl->drawmode = data.u; break;
915 vtl->track_color = data.c;
916 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
918 case PARAM_DP: vtl->drawpoints = data.b; break;
920 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
921 vtl->drawpoints_size = data.u;
923 case PARAM_DE: vtl->drawelevation = data.b; break;
924 case PARAM_DS: vtl->drawstops = data.b; break;
925 case PARAM_DL: vtl->drawlines = data.b; break;
926 case PARAM_DD: vtl->drawdirections = data.b; break;
928 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
929 vtl->drawdirections_size = data.u;
931 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
932 vtl->stop_length = data.u;
934 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
935 vtl->elevation_factor = data.u;
937 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
939 vtl->line_thickness = data.u;
940 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
943 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
945 vtl->bg_line_thickness = data.u;
946 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
950 vtl->track_bg_color = data.c;
951 if ( vtl->track_bg_gc )
952 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
954 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
955 case PARAM_DLA: vtl->drawlabels = data.b; break;
956 case PARAM_DI: vtl->drawimages = data.b; break;
957 case PARAM_IS: if ( data.u != vtl->image_size )
959 vtl->image_size = data.u;
960 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
961 g_queue_free ( vtl->image_cache );
962 vtl->image_cache = g_queue_new ();
965 case PARAM_IA: vtl->image_alpha = data.u; break;
966 case PARAM_ICS: vtl->image_cache_size = data.u;
967 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
968 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
971 vtl->waypoint_color = data.c;
972 if ( vtl->waypoint_gc )
973 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
976 vtl->waypoint_text_color = data.c;
977 if ( vtl->waypoint_text_gc )
978 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
981 vtl->waypoint_bg_color = data.c;
982 if ( vtl->waypoint_bg_gc )
983 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
986 vtl->wpbgand = data.b;
987 if ( vtl->waypoint_bg_gc )
988 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
990 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
991 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
992 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
993 case PARAM_WPFONTSIZE:
994 if ( data.u < FS_NUM_SIZES ) {
995 vtl->wp_font_size = data.u;
996 g_free ( vtl->wp_fsize_str );
997 switch ( vtl->wp_font_size ) {
998 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
999 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1000 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1001 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1002 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1003 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1004 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1012 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1014 VikLayerParamData rv;
1017 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1018 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1019 case PARAM_RV: rv.b = vtl->routes_visible; break;
1020 case PARAM_DM: rv.u = vtl->drawmode; break;
1021 case PARAM_TC: rv.c = vtl->track_color; break;
1022 case PARAM_DP: rv.b = vtl->drawpoints; break;
1023 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1024 case PARAM_DE: rv.b = vtl->drawelevation; break;
1025 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1026 case PARAM_DS: rv.b = vtl->drawstops; break;
1027 case PARAM_SL: rv.u = vtl->stop_length; break;
1028 case PARAM_DL: rv.b = vtl->drawlines; break;
1029 case PARAM_DD: rv.b = vtl->drawdirections; break;
1030 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1031 case PARAM_LT: rv.u = vtl->line_thickness; break;
1032 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1033 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1034 case PARAM_DI: rv.b = vtl->drawimages; break;
1035 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1036 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1037 case PARAM_IS: rv.u = vtl->image_size; break;
1038 case PARAM_IA: rv.u = vtl->image_alpha; break;
1039 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1040 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1041 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1042 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1043 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1044 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1045 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1046 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1047 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1052 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1059 // Use byte arrays to store sublayer data
1060 // much like done elsewhere e.g. vik_layer_marshall_params()
1061 GByteArray *ba = g_byte_array_new ( );
1066 guint object_length;
1069 // the length of the item
1070 // the sublayer type of item
1071 // the the actual item
1072 #define tlm_append(object_pointer, size, type) \
1074 object_length = (size); \
1075 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1076 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1077 g_byte_array_append ( ba, (object_pointer), object_length );
1079 // Layer parameters first
1080 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1081 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1082 g_byte_array_append ( ba, pd, pl );
1085 // Now sublayer data
1086 GHashTableIter iter;
1087 gpointer key, value;
1090 g_hash_table_iter_init ( &iter, vtl->waypoints );
1091 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1092 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1093 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1098 g_hash_table_iter_init ( &iter, vtl->tracks );
1099 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1100 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1101 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1106 g_hash_table_iter_init ( &iter, vtl->routes );
1107 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1108 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1109 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1119 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1121 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1123 gint consumed_length;
1125 // First the overall layer parameters
1126 memcpy(&pl, data, sizeof(pl));
1128 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1131 consumed_length = pl;
1132 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1134 #define tlm_size (*(gint *)data)
1135 // See marshalling above for order of how this is written
1137 data += sizeof_len_and_subtype + tlm_size;
1139 // Now the individual sublayers:
1141 while ( *data && consumed_length < len ) {
1142 // Normally four extra bytes at the end of the datastream
1143 // (since it's a GByteArray and that's where it's length is stored)
1144 // So only attempt read when there's an actual block of sublayer data
1145 if ( consumed_length + tlm_size < len ) {
1147 // Reuse pl to read the subtype from the data stream
1148 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1150 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1151 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1152 gchar *name = g_strdup ( trk->name );
1153 vik_trw_layer_add_track ( vtl, name, trk );
1156 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1157 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1158 gchar *name = g_strdup ( wp->name );
1159 vik_trw_layer_add_waypoint ( vtl, name, wp );
1162 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1163 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1164 gchar *name = g_strdup ( trk->name );
1165 vik_trw_layer_add_route ( vtl, name, trk );
1169 consumed_length += tlm_size + sizeof_len_and_subtype;
1172 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1174 // Not stored anywhere else so need to regenerate
1175 trw_layer_calculate_bounds_waypoints ( vtl );
1180 // Keep interesting hash function at least visible
1182 static guint strcase_hash(gconstpointer v)
1184 // 31 bit hash function
1187 gchar s[128]; // malloc is too slow for reading big files
1190 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1191 p[i] = toupper(t[i]);
1197 for (p += 1; *p != '\0'; p++)
1198 h = (h << 5) - h + *p;
1205 // Stick a 1 at the end of the function name to make it more unique
1206 // thus more easily searchable in a simple text editor
1207 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1209 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1210 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1212 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1213 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1215 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1216 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1217 // and with normal PC processing capabilities - it has negligibile performance impact
1218 // This also minimized the amount of rework - as the management of the hash tables already exists.
1220 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1221 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1222 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1224 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1225 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1226 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1227 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1228 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1229 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1231 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1233 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1235 // Param settings that are not available via the GUI
1236 // Force to on after processing params (which defaults them to off with a zero value)
1237 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1239 rv->draw_sync_done = TRUE;
1240 rv->draw_sync_do = TRUE;
1246 static void trw_layer_free ( VikTrwLayer *trwlayer )
1248 g_hash_table_destroy(trwlayer->waypoints);
1249 g_hash_table_destroy(trwlayer->tracks);
1251 /* ODC: replace with GArray */
1252 trw_layer_free_track_gcs ( trwlayer );
1254 if ( trwlayer->wp_right_click_menu )
1255 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1257 if ( trwlayer->track_right_click_menu )
1258 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1260 if ( trwlayer->wplabellayout != NULL)
1261 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1263 if ( trwlayer->waypoint_gc != NULL )
1264 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1266 if ( trwlayer->waypoint_text_gc != NULL )
1267 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1269 if ( trwlayer->waypoint_bg_gc != NULL )
1270 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1272 g_free ( trwlayer->wp_fsize_str );
1274 if ( trwlayer->tpwin != NULL )
1275 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1277 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1278 g_queue_free ( trwlayer->image_cache );
1281 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1285 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1286 dp->xmpp = vik_viewport_get_xmpp ( vp );
1287 dp->ympp = vik_viewport_get_ympp ( vp );
1288 dp->width = vik_viewport_get_width ( vp );
1289 dp->height = vik_viewport_get_height ( vp );
1290 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1291 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1293 dp->center = vik_viewport_get_center ( vp );
1294 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1295 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1300 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1301 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1302 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1304 dp->ce1 = dp->center->east_west-w2;
1305 dp->ce2 = dp->center->east_west+w2;
1306 dp->cn1 = dp->center->north_south-h2;
1307 dp->cn2 = dp->center->north_south+h2;
1308 } else if ( dp->lat_lon ) {
1309 VikCoord upperleft, bottomright;
1310 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1311 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1312 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1313 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1314 dp->ce1 = upperleft.east_west;
1315 dp->ce2 = bottomright.east_west;
1316 dp->cn1 = bottomright.north_south;
1317 dp->cn2 = upperleft.north_south;
1320 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1324 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1325 * Here a simple traffic like light colour system is used:
1326 * . slow points are red
1327 * . average is yellow
1328 * . fast points are green
1330 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1333 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1334 if ( average_speed > 0 ) {
1335 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1336 if ( rv < low_speed )
1337 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1338 else if ( rv > high_speed )
1339 return VIK_TRW_LAYER_TRACK_GC_FAST;
1341 return VIK_TRW_LAYER_TRACK_GC_AVER;
1344 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1347 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1349 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1350 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1351 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1352 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1355 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1357 if ( ! track->visible )
1360 /* TODO: this function is a mess, get rid of any redundancy */
1361 GList *list = track->trackpoints;
1363 gboolean useoldvals = TRUE;
1365 gboolean drawpoints;
1367 gboolean drawelevation;
1368 gdouble min_alt, max_alt, alt_diff = 0;
1370 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1371 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1374 if ( dp->vtl->drawelevation )
1376 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1377 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1378 alt_diff = max_alt - min_alt;
1381 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1382 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1383 trw_layer_draw_track ( id, track, dp, TRUE );
1385 if ( draw_track_outline )
1386 drawpoints = drawstops = FALSE;
1388 drawpoints = dp->vtl->drawpoints;
1389 drawstops = dp->vtl->drawstops;
1392 gboolean drawing_highlight = FALSE;
1393 /* Current track - used for creation */
1394 if ( track == dp->vtl->current_track )
1395 main_gc = dp->vtl->current_track_gc;
1397 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1398 /* Draw all tracks of the layer in special colour */
1399 /* if track is member of selected layer or is the current selected track
1400 then draw in the highlight colour.
1401 NB this supercedes the drawmode */
1402 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1403 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1404 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1405 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1406 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1407 drawing_highlight = TRUE;
1410 if ( !drawing_highlight ) {
1411 // Still need to figure out the gc according to the drawing mode:
1412 switch ( dp->vtl->drawmode ) {
1413 case DRAWMODE_BY_TRACK:
1414 if ( dp->vtl->track_1color_gc )
1415 g_object_unref ( dp->vtl->track_1color_gc );
1416 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1417 main_gc = dp->vtl->track_1color_gc;
1420 // Mostly for DRAWMODE_ALL_SAME_COLOR
1421 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1422 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1429 int x, y, oldx, oldy;
1430 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1432 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1434 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1436 // Draw the first point as something a bit different from the normal points
1437 // ATM it's slightly bigger and a triangle
1439 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1440 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1446 gdouble average_speed = 0.0;
1447 gdouble low_speed = 0.0;
1448 gdouble high_speed = 0.0;
1449 // If necessary calculate these values - which is done only once per track redraw
1450 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1451 // the percentage factor away from the average speed determines transistions between the levels
1452 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1453 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1454 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1457 while ((list = g_list_next(list)))
1459 tp = VIK_TRACKPOINT(list->data);
1460 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1462 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1463 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1464 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1465 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1466 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1468 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1471 * If points are the same in display coordinates, don't draw.
1473 if ( useoldvals && x == oldx && y == oldy )
1475 // Still need to process points to ensure 'stops' are drawn if required
1476 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1477 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1478 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 );
1483 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1484 if ( drawpoints || dp->vtl->drawlines ) {
1485 // setup main_gc for both point and line drawing
1486 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1487 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 ) );
1491 if ( drawpoints && ! draw_track_outline )
1496 * The concept of drawing stops is that a trackpoint
1497 * that is if the next trackpoint has a timestamp far into
1498 * the future, we draw a circle of 6x trackpoint size,
1499 * instead of a rectangle of 2x trackpoint size.
1500 * This is drawn first so the trackpoint will be drawn on top
1503 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1504 /* Stop point. Draw 6x circle. Always in redish colour */
1505 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 );
1507 /* Regular point - draw 2x square. */
1508 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1511 /* Final point - draw 4x circle. */
1512 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 );
1515 if ((!tp->newsegment) && (dp->vtl->drawlines))
1518 /* UTM only: zone check */
1519 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1520 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1523 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1525 if ( draw_track_outline ) {
1526 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1530 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1532 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1534 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1539 tmp[1].y = oldy-FIXALTITUDE(list->data);
1541 tmp[2].y = y-FIXALTITUDE(list->next->data);
1546 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1547 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1549 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1550 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1552 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1557 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1558 // Draw an arrow at the mid point to show the direction of the track
1559 // Code is a rework from vikwindow::draw_ruler()
1560 gint midx = (oldx + x) / 2;
1561 gint midy = (oldy + y) / 2;
1563 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1564 // Avoid divide by zero and ensure at least 1 pixel big
1566 gdouble dx = (oldx - midx) / len;
1567 gdouble dy = (oldy - midy) / len;
1568 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1569 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1579 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1581 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1582 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1584 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1586 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1587 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 ));
1591 * If points are the same in display coordinates, don't draw.
1593 if ( x != oldx || y != oldy )
1595 if ( draw_track_outline )
1596 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1598 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1604 * If points are the same in display coordinates, don't draw.
1606 if ( x != oldx && y != oldy )
1608 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1609 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1619 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1621 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1622 trw_layer_draw_track ( id, track, dp, FALSE );
1626 static void cached_pixbuf_free ( CachedPixbuf *cp )
1628 g_object_unref ( G_OBJECT(cp->pixbuf) );
1629 g_free ( cp->image );
1632 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1634 return strcmp ( cp->image, name );
1637 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1640 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1641 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1642 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1645 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1647 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1649 if ( wp->image && dp->vtl->drawimages )
1651 GdkPixbuf *pixbuf = NULL;
1654 if ( dp->vtl->image_alpha == 0)
1657 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1659 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1662 gchar *image = wp->image;
1663 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1664 if ( ! regularthumb )
1666 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1667 image = "\x12\x00"; /* this shouldn't occur naturally. */
1671 CachedPixbuf *cp = NULL;
1672 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1673 if ( dp->vtl->image_size == 128 )
1674 cp->pixbuf = regularthumb;
1677 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1678 g_assert ( cp->pixbuf );
1679 g_object_unref ( G_OBJECT(regularthumb) );
1681 cp->image = g_strdup ( image );
1683 /* needed so 'click picture' tool knows how big the pic is; we don't
1684 * store it in cp because they may have been freed already. */
1685 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1686 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1688 g_queue_push_head ( dp->vtl->image_cache, cp );
1689 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1690 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1692 pixbuf = cp->pixbuf;
1696 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1702 w = gdk_pixbuf_get_width ( pixbuf );
1703 h = gdk_pixbuf_get_height ( pixbuf );
1705 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1707 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1708 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1709 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1710 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
1711 // Highlighted - so draw a little border around the chosen one
1712 // single line seems a little weak so draw 2 of them
1713 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1714 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1715 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1716 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1719 if ( dp->vtl->image_alpha == 255 )
1720 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1722 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1724 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1728 // Draw appropriate symbol - either symbol image or simple types
1729 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
1730 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 );
1732 else if ( wp == dp->vtl->current_wp ) {
1733 switch ( dp->vtl->wp_symbol ) {
1734 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;
1735 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;
1736 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;
1737 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 );
1738 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 );
1742 switch ( dp->vtl->wp_symbol ) {
1743 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;
1744 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;
1745 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;
1746 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 );
1747 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;
1751 if ( dp->vtl->drawlabels )
1753 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1754 gint label_x, label_y;
1756 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1758 // Could this stored in the waypoint rather than recreating each pass?
1759 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
1761 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1762 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1764 // Fallback if parse failure
1765 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1767 g_free ( wp_label_markup );
1769 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1770 label_x = x - width/2;
1771 if ( wp->symbol_pixbuf )
1772 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
1774 label_y = y - dp->vtl->wp_size - height - 2;
1776 /* if highlight mode on, then draw background text in highlight colour */
1777 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1778 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1779 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1780 wp == vik_window_get_selected_waypoint ( dp->vw ) )
1781 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1783 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1786 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1788 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1793 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1795 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
1796 trw_layer_draw_waypoint ( id, wp, dp );
1800 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1802 static struct DrawingParams dp;
1803 g_assert ( l != NULL );
1805 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1807 if ( l->tracks_visible )
1808 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1810 if ( l->routes_visible )
1811 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1813 if (l->waypoints_visible)
1814 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
1817 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1820 if ( vtl->track_bg_gc )
1822 g_object_unref ( vtl->track_bg_gc );
1823 vtl->track_bg_gc = NULL;
1825 if ( vtl->track_1color_gc )
1827 g_object_unref ( vtl->track_1color_gc );
1828 vtl->track_1color_gc = NULL;
1830 if ( vtl->current_track_gc )
1832 g_object_unref ( vtl->current_track_gc );
1833 vtl->current_track_gc = NULL;
1835 if ( vtl->current_track_newpoint_gc )
1837 g_object_unref ( vtl->current_track_newpoint_gc );
1838 vtl->current_track_newpoint_gc = NULL;
1841 if ( ! vtl->track_gc )
1843 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1844 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1845 g_array_free ( vtl->track_gc, TRUE );
1846 vtl->track_gc = NULL;
1849 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1851 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1852 gint width = vtl->line_thickness;
1854 if ( vtl->track_gc )
1855 trw_layer_free_track_gcs ( vtl );
1857 if ( vtl->track_bg_gc )
1858 g_object_unref ( vtl->track_bg_gc );
1859 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
1861 // Ensure new track drawing heeds line thickness setting
1862 // however always have a minium of 2, as 1 pixel is really narrow
1863 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1865 if ( vtl->current_track_gc )
1866 g_object_unref ( vtl->current_track_gc );
1867 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1868 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1870 // 'newpoint' gc is exactly the same as the current track gc
1871 if ( vtl->current_track_newpoint_gc )
1872 g_object_unref ( vtl->current_track_newpoint_gc );
1873 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1874 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1876 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1878 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1879 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1881 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1882 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1883 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1885 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
1887 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1890 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1892 VikTrwLayer *rv = trw_layer_new1 ( vp );
1893 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1895 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1896 /* early exit, as the rest is GUI related */
1900 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1901 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
1903 trw_layer_new_track_gcs ( rv, vp );
1905 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
1906 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
1907 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
1908 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
1910 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1912 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1917 #define SMALL_ICON_SIZE 18
1919 * Can accept a null symbol, and may return null value
1921 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1923 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1924 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1925 // So needing a small icon for the treeview may need some resizing:
1926 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1927 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1931 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1933 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1935 GdkPixbuf *pixbuf = NULL;
1937 if ( track->has_color ) {
1938 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1939 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1940 // Here is some magic found to do the conversion
1941 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1942 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1943 ((track->color.green & 0xff00) << 8) |
1944 (track->color.blue & 0xff00);
1946 gdk_pixbuf_fill ( pixbuf, pixel );
1949 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1950 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 );
1952 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 );
1956 g_object_unref (pixbuf);
1958 *new_iter = *((GtkTreeIter *) pass_along[1]);
1959 if ( track->is_route )
1960 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1962 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1964 if ( ! track->visible )
1965 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1968 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1970 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1972 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1973 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 );
1975 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 );
1978 *new_iter = *((GtkTreeIter *) pass_along[1]);
1979 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1981 if ( ! wp->visible )
1982 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1985 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1987 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1988 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1990 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1994 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1996 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1997 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1999 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2003 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2005 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2006 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2008 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2012 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2015 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2017 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2018 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2019 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2021 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
2024 if ( g_hash_table_size (vtl->routes) > 0 ) {
2026 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2028 pass_along[0] = &(vtl->routes_iter);
2029 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2031 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2033 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2036 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2037 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2039 pass_along[0] = &(vtl->waypoints_iter);
2040 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2042 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2044 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2049 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2053 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2054 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2055 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2056 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2058 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2060 return (t->visible ^= 1);
2064 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2066 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2068 return (t->visible ^= 1);
2072 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2074 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2076 return (t->visible ^= 1);
2085 * Return a property about tracks for this layer
2087 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2089 return vtl->line_thickness;
2092 // Structure to hold multiple track information for a layer
2101 * Build up layer multiple track information via updating the tooltip_tracks structure
2103 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2105 tt->length = tt->length + vik_track_get_length (tr);
2107 // Ensure times are available
2108 if ( tr->trackpoints &&
2109 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2110 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2113 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2114 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2116 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2117 // Hence initialize to the first 'proper' value
2118 if ( tt->start_time == 0 )
2119 tt->start_time = t1;
2120 if ( tt->end_time == 0 )
2123 // Update find the earliest / last times
2124 if ( t1 < tt->start_time )
2125 tt->start_time = t1;
2126 if ( t2 > tt->end_time )
2129 // Keep track of total time
2130 // there maybe gaps within a track (eg segments)
2131 // but this should be generally good enough for a simple indicator
2132 tt->duration = tt->duration + (int)(t2-t1);
2137 * Generate tooltip text for the layer.
2138 * This is relatively complicated as it considers information for
2139 * no tracks, a single track or multiple tracks
2140 * (which may or may not have timing information)
2142 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2153 static gchar tmp_buf[128];
2156 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2158 // Safety check - I think these should always be valid
2159 if ( vtl->tracks && vtl->waypoints ) {
2160 tooltip_tracks tt = { 0.0, 0, 0 };
2161 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2163 GDate* gdate_start = g_date_new ();
2164 g_date_set_time_t (gdate_start, tt.start_time);
2166 GDate* gdate_end = g_date_new ();
2167 g_date_set_time_t (gdate_end, tt.end_time);
2169 if ( g_date_compare (gdate_start, gdate_end) ) {
2170 // Dates differ so print range on separate line
2171 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2172 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2173 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2176 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2177 if ( tt.start_time != 0 )
2178 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2182 if ( tt.length > 0.0 ) {
2183 gdouble len_in_units;
2185 // Setup info dependent on distance units
2186 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2187 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2188 len_in_units = VIK_METERS_TO_MILES(tt.length);
2191 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2192 len_in_units = tt.length/1000.0;
2195 // Timing information if available
2197 if ( tt.duration > 0 ) {
2198 g_snprintf (tbuf1, sizeof(tbuf1),
2199 _(" in %d:%02d hrs:mins"),
2200 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2202 g_snprintf (tbuf2, sizeof(tbuf2),
2203 _("\n%sTotal Length %.1f %s%s"),
2204 tbuf3, len_in_units, tbuf4, tbuf1);
2207 // Put together all the elements to form compact tooltip text
2208 g_snprintf (tmp_buf, sizeof(tmp_buf),
2209 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2210 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2212 g_date_free (gdate_start);
2213 g_date_free (gdate_end);
2220 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2224 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2226 // Very simple tooltip - may expand detail in the future...
2227 static gchar tmp_buf[32];
2228 g_snprintf (tmp_buf, sizeof(tmp_buf),
2230 g_hash_table_size (l->tracks));
2234 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2236 // Very simple tooltip - may expand detail in the future...
2237 static gchar tmp_buf[32];
2238 g_snprintf (tmp_buf, sizeof(tmp_buf),
2240 g_hash_table_size (l->routes));
2245 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2246 // Same tooltip for a route
2247 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2250 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2251 tr = g_hash_table_lookup ( l->tracks, sublayer );
2253 tr = g_hash_table_lookup ( l->routes, sublayer );
2256 // Could be a better way of handling strings - but this works...
2257 gchar time_buf1[20];
2258 gchar time_buf2[20];
2259 time_buf1[0] = '\0';
2260 time_buf2[0] = '\0';
2261 static gchar tmp_buf[100];
2262 // Compact info: Short date eg (11/20/99), duration and length
2263 // Hopefully these are the things that are most useful and so promoted into the tooltip
2264 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2265 // %x The preferred date representation for the current locale without the time.
2266 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2267 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2268 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2270 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2273 // Get length and consider the appropriate distance units
2274 gdouble tr_len = vik_track_get_length(tr);
2275 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2276 switch (dist_units) {
2277 case VIK_UNITS_DISTANCE_KILOMETRES:
2278 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2280 case VIK_UNITS_DISTANCE_MILES:
2281 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2290 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2292 // Very simple tooltip - may expand detail in the future...
2293 static gchar tmp_buf[32];
2294 g_snprintf (tmp_buf, sizeof(tmp_buf),
2296 g_hash_table_size (l->waypoints));
2300 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2302 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2303 // NB It's OK to return NULL
2308 return w->description;
2318 * Function to show basic track point information on the statusbar
2320 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2323 switch (a_vik_get_units_height ()) {
2324 case VIK_UNITS_HEIGHT_FEET:
2325 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2328 //VIK_UNITS_HEIGHT_METRES:
2329 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2334 if ( trkpt->has_timestamp ) {
2335 // Compact date time format
2336 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2340 // Position is put later on, as this bit may not be seen if the display is not big enough,
2341 // one can easily use the current pointer position to see this if needed
2342 gchar *lat = NULL, *lon = NULL;
2343 static struct LatLon ll;
2344 vik_coord_to_latlon (&(trkpt->coord), &ll);
2345 a_coords_latlon_to_string ( &ll, &lat, &lon );
2348 // Again is put later on, as this bit may not be seen if the display is not big enough
2349 // trackname can be seen from the treeview (when enabled)
2350 // Also name could be very long to not leave room for anything else
2353 if ( vtl->current_tp_track ) {
2354 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2357 // Combine parts to make overall message
2358 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2359 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2366 * Function to show basic waypoint information on the statusbar
2368 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2371 switch (a_vik_get_units_height ()) {
2372 case VIK_UNITS_HEIGHT_FEET:
2373 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2376 //VIK_UNITS_HEIGHT_METRES:
2377 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2381 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2382 // one can easily use the current pointer position to see this if needed
2383 gchar *lat = NULL, *lon = NULL;
2384 static struct LatLon ll;
2385 vik_coord_to_latlon (&(wpt->coord), &ll);
2386 a_coords_latlon_to_string ( &ll, &lat, &lon );
2388 // Combine parts to make overall message
2391 // Add comment if available
2392 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2394 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2395 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2402 * General layer selection function, find out which bit is selected and take appropriate action
2404 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2407 l->current_wp = NULL;
2408 l->current_wp_id = NULL;
2409 trw_layer_cancel_current_tp ( l, FALSE );
2412 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2416 case VIK_TREEVIEW_TYPE_LAYER:
2418 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2419 /* Mark for redraw */
2424 case VIK_TREEVIEW_TYPE_SUBLAYER:
2428 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2430 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2431 /* Mark for redraw */
2435 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2437 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2438 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2439 /* Mark for redraw */
2443 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2445 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2446 /* Mark for redraw */
2450 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2452 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2453 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2454 /* Mark for redraw */
2458 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2460 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2461 /* Mark for redraw */
2465 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2467 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2469 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2470 // Show some waypoint info
2471 set_statusbar_msg_info_wpt ( l, wpt );
2472 /* Mark for redraw */
2479 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2488 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2493 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2498 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2503 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2505 return l->waypoints;
2508 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2510 return ! ( g_hash_table_size ( vtl->tracks ) ||
2511 g_hash_table_size ( vtl->routes ) ||
2512 g_hash_table_size ( vtl->waypoints ) );
2516 * ATM use a case sensitive find
2517 * Finds the first one
2519 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2521 if ( wp && wp->name )
2522 if ( ! strcmp ( wp->name, name ) )
2528 * Get waypoint by name - not guaranteed to be unique
2529 * Finds the first one
2531 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2533 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2537 * ATM use a case sensitive find
2538 * Finds the first one
2540 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2542 if ( trk && trk->name )
2543 if ( ! strcmp ( trk->name, name ) )
2549 * Get track by name - not guaranteed to be unique
2550 * Finds the first one
2552 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2554 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2558 * Get route by name - not guaranteed to be unique
2559 * Finds the first one
2561 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2563 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2566 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] )
2568 static VikCoord fixme;
2569 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2570 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2571 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2572 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2573 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2574 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2575 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2576 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2577 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2580 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2582 GList *tr = trk->trackpoints;
2583 static VikCoord fixme;
2587 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2588 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2589 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2590 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2591 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2592 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2593 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2594 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2595 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2600 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2602 // Continually reuse maxmin to find the latest maximum and minimum values
2603 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2604 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2605 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2608 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2610 /* 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... */
2611 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2612 trw_layer_find_maxmin (vtl, maxmin);
2613 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2617 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2618 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2623 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2626 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2627 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2629 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2632 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2634 /* First set the center [in case previously viewing from elsewhere] */
2635 /* Then loop through zoom levels until provided positions are in view */
2636 /* This method is not particularly fast - but should work well enough */
2637 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2639 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2640 vik_viewport_set_center_coord ( vvp, &coord );
2642 /* Convert into definite 'smallest' and 'largest' positions */
2643 struct LatLon minmin;
2644 if ( maxmin[0].lat < maxmin[1].lat )
2645 minmin.lat = maxmin[0].lat;
2647 minmin.lat = maxmin[1].lat;
2649 struct LatLon maxmax;
2650 if ( maxmin[0].lon > maxmin[1].lon )
2651 maxmax.lon = maxmin[0].lon;
2653 maxmax.lon = maxmin[1].lon;
2655 /* Never zoom in too far - generally not that useful, as too close ! */
2656 /* Always recalculate the 'best' zoom level */
2658 vik_viewport_set_zoom ( vvp, zoom );
2660 gdouble min_lat, max_lat, min_lon, max_lon;
2661 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2662 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2663 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2664 /* NB I think the logic used in this test to determine if the bounds is within view
2665 fails if track goes across 180 degrees longitude.
2666 Hopefully that situation is not too common...
2667 Mind you viking doesn't really do edge locations to well anyway */
2668 if ( min_lat < minmin.lat &&
2669 max_lat > minmin.lat &&
2670 min_lon < maxmax.lon &&
2671 max_lon > maxmax.lon )
2672 /* Found within zoom level */
2677 vik_viewport_set_zoom ( vvp, zoom );
2681 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2683 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2684 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2685 trw_layer_find_maxmin (vtl, maxmin);
2686 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2689 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2694 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2696 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])) ) ) {
2697 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2700 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2703 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2705 GtkWidget *file_selector;
2707 gboolean failed = FALSE;
2708 file_selector = gtk_file_chooser_dialog_new (title,
2710 GTK_FILE_CHOOSER_ACTION_SAVE,
2711 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2712 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
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 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2727 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2729 gtk_widget_hide ( file_selector );
2730 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2735 gtk_widget_destroy ( file_selector );
2737 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2740 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2742 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2745 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2747 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2750 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2752 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2753 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2754 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2755 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2757 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2759 g_free ( auto_save_name );
2762 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2764 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2765 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2766 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2767 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2769 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2771 g_free ( auto_save_name );
2775 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2778 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2780 gchar *name_used = NULL;
2783 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2784 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2786 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2790 gchar *quoted_file = g_shell_quote ( name_used );
2791 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2792 g_free ( quoted_file );
2793 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2795 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2796 g_error_free ( err );
2800 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2801 //g_remove ( name_used );
2802 // Perhaps should be deleted when the program ends?
2803 // For now leave it to the user to delete it / use system temp cleanup methods.
2804 g_free ( name_used );
2808 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2810 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2813 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2815 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2818 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2820 gpointer layer_and_vlp[2];
2821 layer_and_vlp[0] = pass_along[0];
2822 layer_and_vlp[1] = pass_along[1];
2824 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2826 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2827 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2829 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2831 if ( !trk || !trk->name )
2834 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2835 gchar *auto_save_name = g_strdup ( trk->name );
2836 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2837 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2839 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2841 g_free ( auto_save_name );
2845 VikWaypoint *wp; // input
2846 gpointer uuid; // output
2849 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2851 wpu_udata *user_data = udata;
2852 if ( wp == user_data->wp ) {
2853 user_data->uuid = id;
2859 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2861 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2862 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2863 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2865 GTK_RESPONSE_REJECT,
2867 GTK_RESPONSE_ACCEPT,
2870 GtkWidget *label, *entry;
2871 label = gtk_label_new(_("Waypoint Name:"));
2872 entry = gtk_entry_new();
2874 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2875 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2876 gtk_widget_show_all ( label );
2877 gtk_widget_show_all ( entry );
2879 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2881 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2883 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2884 // Find *first* wp with the given name
2885 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2888 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2891 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2892 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2894 // Find and select on the side panel
2899 // Hmmm, want key of it
2900 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2902 if ( wpf && udata.uuid ) {
2903 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2904 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2913 gtk_widget_destroy ( dia );
2916 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2918 gchar *default_name = highest_wp_number_get(vtl);
2919 VikWaypoint *wp = vik_waypoint_new();
2920 gchar *returned_name;
2922 wp->coord = *def_coord;
2924 // Attempt to auto set height if DEM data is available
2925 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2926 if ( elev != VIK_DEM_INVALID_ELEVATION )
2927 wp->altitude = (gdouble)elev;
2929 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2931 if ( returned_name )
2934 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2935 g_free (default_name);
2936 g_free (returned_name);
2939 g_free (default_name);
2940 vik_waypoint_free(wp);
2944 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2946 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2947 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2948 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2949 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2950 VikViewport *vvp = vik_window_viewport(vw);
2952 // Note the order is max part first then min part - thus reverse order of use in min_max function:
2953 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
2954 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2955 trw_layer_calculate_bounds_waypoints ( vtl );
2956 vik_layers_panel_emit_update ( vlp );
2959 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2961 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2962 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2963 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2965 trw_layer_find_maxmin (vtl, maxmin);
2966 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2967 trw_layer_calculate_bounds_waypoints ( vtl );
2968 vik_layers_panel_emit_update ( vlp );
2971 #ifdef VIK_CONFIG_GEOTAG
2972 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2974 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2976 // Update directly - not changing the mtime
2977 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2980 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2982 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2985 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2989 * Use code in separate file for this feature as reasonably complex
2991 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2993 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2994 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2995 // Unset so can be reverified later if necessary
2996 vtl->has_verified_thumbnails = FALSE;
2998 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3004 static void trw_layer_geotagging ( gpointer lav[2] )
3006 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3007 // Unset so can be reverified later if necessary
3008 vtl->has_verified_thumbnails = FALSE;
3010 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3017 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3020 * Acquire into this TRW Layer straight from GPS Device
3022 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3024 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3025 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3026 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3027 VikViewport *vvp = vik_window_viewport(vw);
3029 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3030 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
3033 #ifdef VIK_CONFIG_GOOGLE
3035 * Acquire into this TRW Layer from Google Directions
3037 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
3039 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3040 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3041 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3042 VikViewport *vvp = vik_window_viewport(vw);
3044 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
3048 #ifdef VIK_CONFIG_OPENSTREETMAP
3050 * Acquire into this TRW Layer from OSM
3052 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3054 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3055 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3056 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3057 VikViewport *vvp = vik_window_viewport(vw);
3059 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
3063 * Acquire into this TRW Layer from OSM for 'My' Traces
3065 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3067 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3068 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3069 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3070 VikViewport *vvp = vik_window_viewport(vw);
3072 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface );
3076 #ifdef VIK_CONFIG_GEOCACHES
3078 * Acquire into this TRW Layer from Geocaching.com
3080 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3082 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3083 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3084 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3085 VikViewport *vvp = vik_window_viewport(vw);
3087 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
3091 #ifdef VIK_CONFIG_GEOTAG
3093 * Acquire into this TRW Layer from images
3095 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3097 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3098 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3099 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3100 VikViewport *vvp = vik_window_viewport(vw);
3102 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3103 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
3105 // Reverify thumbnails as they may have changed
3106 vtl->has_verified_thumbnails = FALSE;
3107 trw_layer_verify_thumbnails ( vtl, NULL );
3111 static void trw_layer_gps_upload ( gpointer lav[2] )
3113 gpointer pass_along[6];
3114 pass_along[0] = lav[0];
3115 pass_along[1] = lav[1];
3116 pass_along[2] = NULL; // No track - operate on the layer
3117 pass_along[3] = NULL;
3118 pass_along[4] = NULL;
3119 pass_along[5] = NULL;
3121 trw_layer_gps_upload_any ( pass_along );
3125 * If pass_along[3] is defined that this will upload just that track
3127 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3129 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3130 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3132 // May not actually get a track here as pass_along[2&3] can be null
3133 VikTrack *track = NULL;
3134 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3135 gboolean xfer_all = FALSE;
3137 if ( pass_along[2] ) {
3139 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3140 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3143 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3144 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3147 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3150 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3154 else if ( !pass_along[4] )
3155 xfer_all = TRUE; // i.e. whole layer
3157 if (track && !track->visible) {
3158 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3162 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3163 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3164 GTK_DIALOG_DESTROY_WITH_PARENT,
3166 GTK_RESPONSE_ACCEPT,
3168 GTK_RESPONSE_REJECT,
3171 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3172 GtkWidget *response_w = NULL;
3173 #if GTK_CHECK_VERSION (2, 20, 0)
3174 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3178 gtk_widget_grab_focus ( response_w );
3180 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3182 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3183 datasource_gps_clean_up ( dgs );
3184 gtk_widget_destroy ( dialog );
3188 // Get info from reused datasource dialog widgets
3189 gchar* protocol = datasource_gps_get_protocol ( dgs );
3190 gchar* port = datasource_gps_get_descriptor ( dgs );
3191 // NB don't free the above strings as they're references to values held elsewhere
3192 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3193 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3194 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3195 gboolean turn_off = datasource_gps_get_off ( dgs );
3197 gtk_widget_destroy ( dialog );
3199 // When called from the viewport - work the corresponding layerspanel:
3201 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3204 // Apply settings to transfer to the GPS device
3211 vik_layers_panel_get_viewport (vlp),
3220 * Acquire into this TRW Layer from any GPS Babel supported file
3222 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3224 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3225 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3226 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3227 VikViewport *vvp = vik_window_viewport(vw);
3229 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3232 static void trw_layer_new_wp ( gpointer lav[2] )
3234 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3235 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3236 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3237 instead return true if you want to update. */
3238 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 ) {
3239 trw_layer_calculate_bounds_waypoints ( vtl );
3240 vik_layers_panel_emit_update ( vlp );
3244 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3246 vtl->current_track = vik_track_new();
3247 vtl->current_track->visible = TRUE;
3248 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3249 // Create track with the preferred colour from the layer properties
3250 vtl->current_track->color = vtl->track_color;
3252 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3253 vtl->current_track->has_color = TRUE;
3254 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3257 static void trw_layer_new_track ( gpointer lav[2] )
3259 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3261 if ( ! vtl->current_track ) {
3262 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3263 new_track_create_common ( vtl, name );
3265 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3269 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3271 vtl->current_track = vik_track_new();
3272 vtl->current_track->visible = TRUE;
3273 vtl->current_track->is_route = TRUE;
3274 // By default make all routes red
3275 vtl->current_track->has_color = TRUE;
3276 gdk_color_parse ( "red", &vtl->current_track->color );
3277 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3280 static void trw_layer_new_route ( gpointer lav[2] )
3282 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3284 if ( ! vtl->current_track ) {
3285 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3286 new_route_create_common ( vtl, name );
3287 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3291 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3293 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3294 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3296 if ( g_hash_table_size (vtl->routes) > 0 ) {
3297 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3298 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3299 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3300 vik_layers_panel_emit_update ( vlp );
3305 static void trw_layer_finish_track ( gpointer lav[2] )
3307 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3308 vtl->current_track = NULL;
3309 vik_layer_emit_update ( VIK_LAYER(vtl) );
3312 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3314 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3315 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3317 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3318 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3319 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3320 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3321 vik_layers_panel_emit_update ( vlp );
3325 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3327 /* NB do not care if wp is visible or not */
3328 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3331 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3333 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3334 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3336 /* Only 1 waypoint - jump straight to it */
3337 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3338 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3339 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3341 /* If at least 2 waypoints - find center and then zoom to fit */
3342 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3344 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3345 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3346 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3349 vik_layers_panel_emit_update ( vlp );
3352 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3354 static gpointer pass_along[2];
3356 GtkWidget *export_submenu;
3357 pass_along[0] = vtl;
3358 pass_along[1] = vlp;
3360 item = gtk_menu_item_new();
3361 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3362 gtk_widget_show ( item );
3364 if ( vtl->current_track ) {
3365 if ( vtl->current_track->is_route )
3366 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3368 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3369 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3370 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3371 gtk_widget_show ( item );
3374 item = gtk_menu_item_new ();
3375 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3376 gtk_widget_show ( item );
3379 /* Now with icons */
3380 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3381 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3382 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3383 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3384 gtk_widget_show ( item );
3386 GtkWidget *view_submenu = gtk_menu_new();
3387 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3388 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3389 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3390 gtk_widget_show ( item );
3391 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3393 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3395 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3396 gtk_widget_show ( item );
3398 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3399 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3400 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3401 gtk_widget_show ( item );
3403 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3404 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3405 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3406 gtk_widget_show ( item );
3408 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3409 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3410 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3411 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3412 gtk_widget_show ( item );
3414 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3415 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3416 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3417 gtk_widget_show ( item );
3419 export_submenu = gtk_menu_new ();
3420 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3421 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3422 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3423 gtk_widget_show ( item );
3424 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3426 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3427 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3428 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3429 gtk_widget_show ( item );
3431 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3432 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3433 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3434 gtk_widget_show ( item );
3436 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3438 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3439 gtk_widget_show ( item );
3441 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3442 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3443 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3444 gtk_widget_show ( item );
3446 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3447 item = gtk_menu_item_new_with_mnemonic ( external1 );
3448 g_free ( external1 );
3449 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3450 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3451 gtk_widget_show ( item );
3453 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3454 item = gtk_menu_item_new_with_mnemonic ( external2 );
3455 g_free ( external2 );
3456 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3457 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3458 gtk_widget_show ( item );
3460 GtkWidget *new_submenu = gtk_menu_new();
3461 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3462 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3463 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3464 gtk_widget_show(item);
3465 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3467 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3468 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3469 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3470 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3471 gtk_widget_show ( item );
3473 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
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_track), pass_along );
3476 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3477 gtk_widget_show ( item );
3478 // Make it available only when a new track *not* already in progress
3479 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3481 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3482 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3483 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3484 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3485 gtk_widget_show ( item );
3486 // Make it available only when a new track *not* already in progress
3487 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3489 #ifdef VIK_CONFIG_GEOTAG
3490 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3491 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3492 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3493 gtk_widget_show ( item );
3496 GtkWidget *acquire_submenu = gtk_menu_new ();
3497 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3498 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3499 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3500 gtk_widget_show ( item );
3501 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3503 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3505 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3506 gtk_widget_show ( item );
3508 #ifdef VIK_CONFIG_GOOGLE
3509 item = gtk_menu_item_new_with_mnemonic ( _("From Google _Directions...") );
3510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3511 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3512 gtk_widget_show ( item );
3515 #ifdef VIK_CONFIG_OPENSTREETMAP
3516 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3517 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3518 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3519 gtk_widget_show ( item );
3521 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3522 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3523 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3524 gtk_widget_show ( item );
3527 #ifdef VIK_CONFIG_GEONAMES
3528 GtkWidget *wikipedia_submenu = gtk_menu_new();
3529 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3530 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3531 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3532 gtk_widget_show(item);
3533 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3535 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3536 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3537 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3538 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3539 gtk_widget_show ( item );
3541 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3542 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3543 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3544 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3545 gtk_widget_show ( item );
3548 #ifdef VIK_CONFIG_GEOCACHES
3549 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3550 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3551 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3552 gtk_widget_show ( item );
3555 #ifdef VIK_CONFIG_GEOTAG
3556 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3557 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3558 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3559 gtk_widget_show ( item );
3562 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3563 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3564 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3565 gtk_widget_show ( item );
3567 GtkWidget *upload_submenu = gtk_menu_new ();
3568 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3569 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3570 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3571 gtk_widget_show ( item );
3572 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3574 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3575 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3576 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3577 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3578 gtk_widget_show ( item );
3580 #ifdef VIK_CONFIG_OPENSTREETMAP
3581 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3582 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3584 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3585 gtk_widget_show ( item );
3588 GtkWidget *delete_submenu = gtk_menu_new ();
3589 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3590 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3591 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3592 gtk_widget_show ( item );
3593 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3595 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3596 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3597 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3598 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3599 gtk_widget_show ( item );
3601 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3602 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3604 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3605 gtk_widget_show ( item );
3607 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3608 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3609 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3610 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3611 gtk_widget_show ( item );
3613 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3614 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3615 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3616 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3617 gtk_widget_show ( item );
3619 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3620 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3621 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3622 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3623 gtk_widget_show ( item );
3625 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3626 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3627 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3628 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3629 gtk_widget_show ( item );
3631 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3632 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3634 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3635 gtk_widget_show ( item );
3638 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3639 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3641 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3642 gtk_widget_show ( item );
3646 // Fake Waypoint UUIDs vi simple increasing integer
3647 static guint wp_uuid = 0;
3649 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3653 vik_waypoint_set_name (wp, name);
3655 if ( VIK_LAYER(vtl)->realized )
3657 // Do we need to create the sublayer:
3658 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3659 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3662 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3664 // Visibility column always needed for waypoints
3665 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3666 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 );
3668 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 );
3670 // Actual setting of visibility dependent on the waypoint
3671 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3673 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3676 highest_wp_number_add_wp(vtl, name);
3677 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3681 // Fake Track UUIDs vi simple increasing integer
3682 static guint tr_uuid = 0;
3684 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3688 vik_track_set_name (t, name);
3690 if ( VIK_LAYER(vtl)->realized )
3692 // Do we need to create the sublayer:
3693 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3694 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3697 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3698 // Visibility column always needed for tracks
3699 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3700 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 );
3702 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 );
3704 // Actual setting of visibility dependent on the track
3705 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3707 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3710 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3712 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3715 // Fake Route UUIDs vi simple increasing integer
3716 static guint rt_uuid = 0;
3718 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3722 vik_track_set_name (t, name);
3724 if ( VIK_LAYER(vtl)->realized )
3726 // Do we need to create the sublayer:
3727 if ( g_hash_table_size (vtl->routes) == 0 ) {
3728 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3731 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3732 // Visibility column always needed for tracks
3733 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3734 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 );
3736 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 );
3738 // Actual setting of visibility dependent on the track
3739 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3741 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3744 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3746 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3749 /* to be called whenever a track has been deleted or may have been changed. */
3750 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3752 if (vtl->current_tp_track == trk )
3753 trw_layer_cancel_current_tp ( vtl, FALSE );
3757 * Normally this is done to due the waypoint size preference having changed
3759 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
3761 GHashTableIter iter;
3762 gpointer key, value;
3765 g_hash_table_iter_init ( &iter, vtl->waypoints );
3766 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
3767 VikWaypoint *wp = VIK_WAYPOINT(value);
3769 // Reapply symbol setting to update the pixbuf
3770 gchar *tmp_symbol = g_strdup ( wp->symbol );
3771 vik_waypoint_set_symbol ( wp, tmp_symbol );
3772 g_free ( tmp_symbol );
3777 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3780 gchar *newname = g_strdup(name);
3785 switch ( sublayer_type ) {
3786 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3787 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3789 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3790 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3793 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3796 // If found a name already in use try adding 1 to it and we try again
3798 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3800 newname = new_newname;
3803 } while ( id != NULL);
3808 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3810 // No more uniqueness of name forced when loading from a file
3811 // This now makes this function a little redunant as we just flow the parameters through
3812 vik_trw_layer_add_waypoint ( vtl, name, wp );
3815 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3817 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3818 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3819 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3820 vik_track_free ( tr );
3821 vtl->route_finder_append = FALSE; /* this means we have added it */
3824 // No more uniqueness of name forced when loading from a file
3826 vik_trw_layer_add_route ( vtl, name, tr );
3828 vik_trw_layer_add_track ( vtl, name, tr );
3830 if ( vtl->route_finder_check_added_track ) {
3831 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3832 vtl->route_finder_added_track = tr;
3837 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3839 *l = g_list_append(*l, id);
3843 * Move an item from one TRW layer to another TRW layer
3845 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3847 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3848 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3850 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3852 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3853 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3854 vik_trw_layer_delete_track ( vtl_src, trk );
3857 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3858 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3860 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3862 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3863 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3864 vik_trw_layer_delete_route ( vtl_src, trk );
3867 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3868 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3870 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3872 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3873 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3874 trw_layer_delete_waypoint ( vtl_src, wp );
3876 trw_layer_calculate_bounds_waypoints ( vtl_dest );
3877 trw_layer_calculate_bounds_waypoints ( vtl_src );
3881 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3883 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3884 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3886 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3887 GList *items = NULL;
3890 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3891 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3893 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3894 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3896 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3897 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3902 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3903 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3905 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3906 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3908 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3915 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3916 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3921 VikTrack *trk; // input
3922 gpointer uuid; // output
3925 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3927 trku_udata *user_data = udata;
3928 if ( trk == user_data->trk ) {
3929 user_data->uuid = id;
3935 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3937 gboolean was_visible = FALSE;
3939 if ( trk && trk->name ) {
3941 if ( trk == vtl->current_track ) {
3942 vtl->current_track = NULL;
3943 vtl->current_tp_track = NULL;
3944 vtl->current_tp_id = NULL;
3945 vtl->moving_tp = FALSE;
3948 was_visible = trk->visible;
3950 if ( trk == vtl->route_finder_current_track )
3951 vtl->route_finder_current_track = NULL;
3953 if ( trk == vtl->route_finder_added_track )
3954 vtl->route_finder_added_track = NULL;
3960 // Hmmm, want key of it
3961 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3963 if ( trkf && udata.uuid ) {
3964 /* could be current_tp, so we have to check */
3965 trw_layer_cancel_tps_of_track ( vtl, trk );
3967 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3970 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3971 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3972 g_hash_table_remove ( vtl->tracks, udata.uuid );
3974 // If last sublayer, then remove sublayer container
3975 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3976 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3984 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
3986 gboolean was_visible = FALSE;
3988 if ( trk && trk->name ) {
3990 if ( trk == vtl->current_track ) {
3991 vtl->current_track = NULL;
3992 vtl->current_tp_track = NULL;
3993 vtl->current_tp_id = NULL;
3994 vtl->moving_tp = FALSE;
3997 was_visible = trk->visible;
3999 if ( trk == vtl->route_finder_current_track )
4000 vtl->route_finder_current_track = NULL;
4002 if ( trk == vtl->route_finder_added_track )
4003 vtl->route_finder_added_track = NULL;
4009 // Hmmm, want key of it
4010 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4012 if ( trkf && udata.uuid ) {
4013 /* could be current_tp, so we have to check */
4014 trw_layer_cancel_tps_of_track ( vtl, trk );
4016 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4019 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4020 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4021 g_hash_table_remove ( vtl->routes, udata.uuid );
4023 // If last sublayer, then remove sublayer container
4024 if ( g_hash_table_size (vtl->routes) == 0 ) {
4025 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4033 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4035 gboolean was_visible = FALSE;
4037 if ( wp && wp->name ) {
4039 if ( wp == vtl->current_wp ) {
4040 vtl->current_wp = NULL;
4041 vtl->current_wp_id = NULL;
4042 vtl->moving_wp = FALSE;
4045 was_visible = wp->visible;
4051 // Hmmm, want key of it
4052 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4054 if ( wpf && udata.uuid ) {
4055 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4058 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4059 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4061 highest_wp_number_remove_wp(vtl, wp->name);
4062 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4064 // If last sublayer, then remove sublayer container
4065 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4066 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4076 // Only for temporary use by trw_layer_delete_waypoint_by_name
4077 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4079 wpu_udata *user_data = udata;
4080 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4081 user_data->uuid = id;
4088 * Delete a waypoint by the given name
4089 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4090 * as there be multiple waypoints with the same name
4092 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4095 // Fake a waypoint with the given name
4096 udata.wp = vik_waypoint_new ();
4097 vik_waypoint_set_name (udata.wp, name);
4098 // Currently only the name is used in this waypoint find function
4101 // Hmmm, want key of it
4102 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4104 vik_waypoint_free (udata.wp);
4106 if ( wpf && udata.uuid )
4107 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4113 VikTrack *trk; // input
4114 gpointer uuid; // output
4117 // Only for temporary use by trw_layer_delete_track_by_name
4118 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4120 tpu_udata *user_data = udata;
4121 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4122 user_data->uuid = id;
4129 * Delete a track by the given name
4130 * NOTE: ATM this will delete the first encountered Track with the specified name
4131 * as there may be multiple tracks with the same name within the specified hash table
4133 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4136 // Fake a track with the given name
4137 udata.trk = vik_track_new ();
4138 vik_track_set_name (udata.trk, name);
4139 // Currently only the name is used in this waypoint find function
4142 // Hmmm, want key of it
4143 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4145 vik_track_free (udata.trk);
4147 if ( trkf && udata.uuid ) {
4148 // This could be a little better written...
4149 if ( vtl->tracks == ht_tracks )
4150 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4151 if ( vtl->routes == ht_tracks )
4152 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4159 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4161 vik_treeview_item_delete (vt, it );
4164 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4167 vtl->current_track = NULL;
4168 vtl->route_finder_current_track = NULL;
4169 vtl->route_finder_added_track = NULL;
4170 if (vtl->current_tp_track)
4171 trw_layer_cancel_current_tp(vtl, FALSE);
4173 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4174 g_hash_table_remove_all(vtl->routes_iters);
4175 g_hash_table_remove_all(vtl->routes);
4177 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4179 vik_layer_emit_update ( VIK_LAYER(vtl) );
4182 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4185 vtl->current_track = NULL;
4186 vtl->route_finder_current_track = NULL;
4187 vtl->route_finder_added_track = NULL;
4188 if (vtl->current_tp_track)
4189 trw_layer_cancel_current_tp(vtl, FALSE);
4191 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4192 g_hash_table_remove_all(vtl->tracks_iters);
4193 g_hash_table_remove_all(vtl->tracks);
4195 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4197 vik_layer_emit_update ( VIK_LAYER(vtl) );
4200 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4202 vtl->current_wp = NULL;
4203 vtl->current_wp_id = NULL;
4204 vtl->moving_wp = FALSE;
4206 highest_wp_number_reset(vtl);
4208 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4209 g_hash_table_remove_all(vtl->waypoints_iters);
4210 g_hash_table_remove_all(vtl->waypoints);
4212 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4214 vik_layer_emit_update ( VIK_LAYER(vtl) );
4217 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4219 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4220 // Get confirmation from the user
4221 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4222 _("Are you sure you want to delete all tracks in %s?"),
4223 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4224 vik_trw_layer_delete_all_tracks (vtl);
4227 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4229 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4230 // Get confirmation from the user
4231 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4232 _("Are you sure you want to delete all routes in %s?"),
4233 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4234 vik_trw_layer_delete_all_routes (vtl);
4237 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4239 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4240 // Get confirmation from the user
4241 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4242 _("Are you sure you want to delete all waypoints in %s?"),
4243 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4244 vik_trw_layer_delete_all_waypoints (vtl);
4247 static void trw_layer_delete_item ( gpointer pass_along[6] )
4249 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4250 gboolean was_visible = FALSE;
4251 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4253 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4254 if ( wp && wp->name ) {
4255 if ( GPOINTER_TO_INT ( pass_along[4]) )
4256 // Get confirmation from the user
4257 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4258 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4259 _("Are you sure you want to delete the waypoint \"%s\""),
4262 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4265 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4267 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4268 if ( trk && trk->name ) {
4269 if ( GPOINTER_TO_INT ( pass_along[4]) )
4270 // Get confirmation from the user
4271 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4272 _("Are you sure you want to delete the track \"%s\""),
4275 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4280 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4281 if ( trk && trk->name ) {
4282 if ( GPOINTER_TO_INT ( pass_along[4]) )
4283 // Get confirmation from the user
4284 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4285 _("Are you sure you want to delete the route \"%s\""),
4288 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4292 vik_layer_emit_update ( VIK_LAYER(vtl) );
4296 static void trw_layer_properties_item ( gpointer pass_along[7] )
4298 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4299 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4301 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4303 if ( wp && wp->name )
4305 gboolean updated = FALSE;
4306 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
4308 if ( updated && pass_along[6] )
4309 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4311 if ( updated && VIK_LAYER(vtl)->visible )
4312 vik_layer_emit_update ( VIK_LAYER(vtl) );
4318 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4319 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4321 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4323 if ( tr && tr->name )
4325 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4328 pass_along[1], /* vlp */
4329 pass_along[5], /* vvp */
4330 pass_along[6]); /* iter */
4336 * Update the treeview of the track id - primarily to update the icon
4338 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4344 gpointer *trkf = NULL;
4345 if ( trk->is_route )
4346 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4348 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4350 if ( trkf && udata.uuid ) {
4352 GtkTreeIter *iter = NULL;
4353 if ( trk->is_route )
4354 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4356 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4359 // TODO: Make this a function
4360 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4361 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4362 ((trk->color.green & 0xff00) << 8) |
4363 (trk->color.blue & 0xff00);
4364 gdk_pixbuf_fill ( pixbuf, pixel );
4365 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4366 g_object_unref (pixbuf);
4373 Parameter 1 -> VikLayersPanel
4374 Parameter 2 -> VikLayer
4375 Parameter 3 -> VikViewport
4377 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4380 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4381 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4384 /* since vlp not set, vl & vvp should be valid instead! */
4386 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4387 vik_layer_emit_update ( VIK_LAYER(vl) );
4392 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4394 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4396 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4397 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4399 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4401 if ( track && track->trackpoints )
4402 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4405 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4407 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4409 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4410 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4412 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4414 if ( track && track->trackpoints )
4416 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4418 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4419 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4420 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4421 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4422 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4426 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4428 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4430 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4431 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4433 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4438 // Converting a track to a route can be a bit more complicated,
4439 // so give a chance to change our minds:
4440 if ( !trk->is_route &&
4441 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4442 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4444 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4445 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4450 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4453 trk_copy->is_route = !trk_copy->is_route;
4455 // ATM can't set name to self - so must create temporary copy
4456 gchar *name = g_strdup ( trk_copy->name );
4458 // Delete old one and then add new one
4459 if ( trk->is_route ) {
4460 vik_trw_layer_delete_route ( vtl, trk );
4461 vik_trw_layer_add_track ( vtl, name, trk_copy );
4464 // Extra route conversion bits...
4465 vik_track_merge_segments ( trk_copy );
4466 vik_track_to_routepoints ( trk_copy );
4468 vik_trw_layer_delete_track ( vtl, trk );
4469 vik_trw_layer_add_route ( vtl, name, trk_copy );
4473 // Update in case color of track / route changes when moving between sublayers
4474 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4478 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4480 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4482 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4483 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4485 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4490 vtl->current_track = track;
4491 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);
4493 if ( track->trackpoints )
4494 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4497 #ifdef VIK_CONFIG_GOOGLE
4499 * extend a track using route finder
4501 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4503 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4504 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4507 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4509 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4510 vtl->route_finder_coord = last_coord;
4511 vtl->route_finder_current_track = track;
4512 vtl->route_finder_started = TRUE;
4514 if ( track->trackpoints )
4515 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4520 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4522 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4523 /* Also warn if overwrite old elevation data */
4524 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4526 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4527 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4529 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4532 vik_track_apply_dem_data ( track );
4535 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4537 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4539 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4540 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4542 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4547 GList *trps = track->trackpoints;
4550 trps = g_list_last(trps);
4551 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4554 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4556 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4558 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4559 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4561 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4566 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4569 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4572 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4574 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4576 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4577 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4579 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4584 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4587 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4590 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4592 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4594 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4595 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4597 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4602 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4605 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4609 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4611 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4613 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4615 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4616 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4618 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4620 if ( trk && trk->trackpoints )
4622 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4623 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4624 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4625 if ( pass_along[1] )
4626 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4628 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4632 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4634 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4635 trw_layer_tpwin_init ( vtl );
4638 /*************************************
4639 * merge/split by time routines
4640 *************************************/
4642 /* called for each key in track hash table.
4643 * If the current track has the same time stamp type, add it to the result,
4644 * except the one pointed by "exclude".
4645 * set exclude to NULL if there is no exclude to check.
4646 * Note that the result is in reverse (for performance reasons).
4651 gboolean with_timestamps;
4653 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4655 twt_udata *user_data = udata;
4656 VikTrackpoint *p1, *p2;
4658 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4662 if (VIK_TRACK(value)->trackpoints) {
4663 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4664 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4666 if ( user_data->with_timestamps ) {
4667 if (!p1->has_timestamp || !p2->has_timestamp) {
4672 // Don't add tracks with timestamps when getting non timestamp tracks
4673 if (p1->has_timestamp || p2->has_timestamp) {
4679 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4682 /* called for each key in track hash table. if original track user_data[1] is close enough
4683 * to the passed one, add it to list in user_data[0]
4685 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4688 VikTrackpoint *p1, *p2;
4689 VikTrack *trk = VIK_TRACK(value);
4691 GList **nearby_tracks = ((gpointer *)user_data)[0];
4692 GList *tpoints = ((gpointer *)user_data)[1];
4695 * detect reasons for not merging, and return
4696 * if no reason is found not to merge, then do it.
4699 // Exclude the original track from the compiled list
4700 if (trk->trackpoints == tpoints) {
4704 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4705 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4707 if (trk->trackpoints) {
4708 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4709 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4711 if (!p1->has_timestamp || !p2->has_timestamp) {
4712 //g_print("no timestamp\n");
4716 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4717 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4718 if (! (abs(t1 - p2->timestamp) < threshold ||
4720 abs(p1->timestamp - t2) < threshold)
4727 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4730 /* comparison function used to sort tracks; a and b are hash table keys */
4731 /* Not actively used - can be restored if needed
4732 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4734 GHashTable *tracks = user_data;
4737 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4738 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4740 if (t1 < t2) return -1;
4741 if (t1 > t2) return 1;
4746 /* comparison function used to sort trackpoints */
4747 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4749 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4751 if (t1 < t2) return -1;
4752 if (t1 > t2) return 1;
4757 * comparison function which can be used to sort tracks or waypoints by name
4759 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4761 const gchar* namea = (const gchar*) a;
4762 const gchar* nameb = (const gchar*) b;
4763 if ( namea == NULL || nameb == NULL)
4766 // Same sort method as used in the vik_treeview_*_alphabetize functions
4767 return strcmp ( namea, nameb );
4771 * Attempt to merge selected track with other tracks specified by the user
4772 * Tracks to merge with must be of the same 'type' as the selected track -
4773 * either all with timestamps, or all without timestamps
4775 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4777 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4778 GList *other_tracks = NULL;
4779 GHashTable *ght_tracks;
4780 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4781 ght_tracks = vtl->routes;
4783 ght_tracks = vtl->tracks;
4785 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4790 if ( !track->trackpoints )
4794 udata.result = &other_tracks;
4795 udata.exclude = track->trackpoints;
4796 // Allow merging with 'similar' time type time tracks
4797 // i.e. either those times, or those without
4798 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4800 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4801 other_tracks = g_list_reverse(other_tracks);
4803 if ( !other_tracks ) {
4804 if ( udata.with_timestamps )
4805 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4807 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4811 // Sort alphabetically for user presentation
4812 // Convert into list of names for usage with dialog function
4813 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4814 GList *other_tracks_names = NULL;
4815 GList *iter = g_list_first ( other_tracks );
4817 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4818 iter = g_list_next ( iter );
4821 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4823 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4827 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4828 g_list_free(other_tracks);
4829 g_list_free(other_tracks_names);
4834 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4835 VikTrack *merge_track;
4836 if ( track->is_route )
4837 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4839 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4842 vik_track_steal_and_append_trackpoints ( track, merge_track );
4843 if ( track->is_route )
4844 vik_trw_layer_delete_route (vtl, merge_track);
4846 vik_trw_layer_delete_track (vtl, merge_track);
4847 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4850 for (l = merge_list; l != NULL; l = g_list_next(l))
4852 g_list_free(merge_list);
4854 vik_layer_emit_update( VIK_LAYER(vtl) );
4858 // c.f. trw_layer_sorted_track_id_by_name_list
4859 // but don't add the specified track to the list (normally current track)
4860 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4862 twt_udata *user_data = udata;
4865 if (trk->trackpoints == user_data->exclude) {
4869 // Sort named list alphabetically
4870 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4874 * Join - this allows combining 'tracks' and 'track routes'
4875 * i.e. doesn't care about whether tracks have consistent timestamps
4876 * ATM can only append one track at a time to the currently selected track
4878 static void trw_layer_append_track ( gpointer pass_along[6] )
4881 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4883 GHashTable *ght_tracks;
4884 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4885 ght_tracks = vtl->routes;
4887 ght_tracks = vtl->tracks;
4889 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4894 GList *other_tracks_names = NULL;
4896 // Sort alphabetically for user presentation
4897 // Convert into list of names for usage with dialog function
4898 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4900 udata.result = &other_tracks_names;
4901 udata.exclude = trk->trackpoints;
4903 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4905 // Note the limit to selecting one track only
4906 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4907 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4908 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4911 trk->is_route ? _("Append Route"): _("Append Track"),
4912 trk->is_route ? _("Select the route to append after the current route") :
4913 _("Select the track to append after the current track") );
4915 g_list_free(other_tracks_names);
4917 // It's a list, but shouldn't contain more than one other track!
4918 if ( append_list ) {
4920 for (l = append_list; l != NULL; l = g_list_next(l)) {
4921 // TODO: at present this uses the first track found by name,
4922 // which with potential multiple same named tracks may not be the one selected...
4923 VikTrack *append_track;
4924 if ( trk->is_route )
4925 append_track = vik_trw_layer_get_route ( vtl, l->data );
4927 append_track = vik_trw_layer_get_track ( vtl, l->data );
4929 if ( append_track ) {
4930 vik_track_steal_and_append_trackpoints ( trk, append_track );
4931 if ( trk->is_route )
4932 vik_trw_layer_delete_route (vtl, append_track);
4934 vik_trw_layer_delete_track (vtl, append_track);
4937 for (l = append_list; l != NULL; l = g_list_next(l))
4939 g_list_free(append_list);
4941 vik_layer_emit_update( VIK_LAYER(vtl) );
4946 * Very similar to trw_layer_append_track for joining
4947 * but this allows selection from the 'other' list
4948 * If a track is selected, then is shows routes and joins the selected one
4949 * If a route is selected, then is shows tracks and joins the selected one
4951 static void trw_layer_append_other ( gpointer pass_along[6] )
4954 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4956 GHashTable *ght_mykind, *ght_others;
4957 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4958 ght_mykind = vtl->routes;
4959 ght_others = vtl->tracks;
4962 ght_mykind = vtl->tracks;
4963 ght_others = vtl->routes;
4966 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
4971 GList *other_tracks_names = NULL;
4973 // Sort alphabetically for user presentation
4974 // Convert into list of names for usage with dialog function
4975 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4977 udata.result = &other_tracks_names;
4978 udata.exclude = trk->trackpoints;
4980 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4982 // Note the limit to selecting one track only
4983 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4984 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4985 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4988 trk->is_route ? _("Append Track"): _("Append Route"),
4989 trk->is_route ? _("Select the track to append after the current route") :
4990 _("Select the route to append after the current track") );
4992 g_list_free(other_tracks_names);
4994 // It's a list, but shouldn't contain more than one other track!
4995 if ( append_list ) {
4997 for (l = append_list; l != NULL; l = g_list_next(l)) {
4998 // TODO: at present this uses the first track found by name,
4999 // which with potential multiple same named tracks may not be the one selected...
5001 // Get FROM THE OTHER TYPE list
5002 VikTrack *append_track;
5003 if ( trk->is_route )
5004 append_track = vik_trw_layer_get_track ( vtl, l->data );
5006 append_track = vik_trw_layer_get_route ( vtl, l->data );
5008 if ( append_track ) {
5010 if ( !append_track->is_route &&
5011 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5012 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5014 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5015 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5016 vik_track_merge_segments ( append_track );
5017 vik_track_to_routepoints ( append_track );
5024 vik_track_steal_and_append_trackpoints ( trk, append_track );
5026 // Delete copied which is FROM THE OTHER TYPE list
5027 if ( trk->is_route )
5028 vik_trw_layer_delete_track (vtl, append_track);
5030 vik_trw_layer_delete_route (vtl, append_track);
5033 for (l = append_list; l != NULL; l = g_list_next(l))
5035 g_list_free(append_list);
5036 vik_layer_emit_update( VIK_LAYER(vtl) );
5040 /* merge by segments */
5041 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5043 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5044 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5045 guint segments = vik_track_merge_segments ( trk );
5046 // NB currently no need to redraw as segments not actually shown on the display
5047 // However inform the user of what happened:
5049 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5050 g_snprintf(str, 64, tmp_str, segments);
5051 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5054 /* merge by time routine */
5055 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5057 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5061 GList *tracks_with_timestamp = NULL;
5062 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5063 if (orig_trk->trackpoints &&
5064 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5065 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5070 udata.result = &tracks_with_timestamp;
5071 udata.exclude = orig_trk->trackpoints;
5072 udata.with_timestamps = TRUE;
5073 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5074 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5076 if (!tracks_with_timestamp) {
5077 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5080 g_list_free(tracks_with_timestamp);
5082 static guint threshold_in_minutes = 1;
5083 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5084 _("Merge Threshold..."),
5085 _("Merge when time between tracks less than:"),
5086 &threshold_in_minutes)) {
5090 // keep attempting to merge all tracks until no merges within the time specified is possible
5091 gboolean attempt_merge = TRUE;
5092 GList *nearby_tracks = NULL;
5094 static gpointer params[3];
5096 while ( attempt_merge ) {
5098 // Don't try again unless tracks have changed
5099 attempt_merge = FALSE;
5101 trps = orig_trk->trackpoints;
5105 if (nearby_tracks) {
5106 g_list_free(nearby_tracks);
5107 nearby_tracks = NULL;
5110 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5111 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5113 /* g_print("Original track times: %d and %d\n", t1, t2); */
5114 params[0] = &nearby_tracks;
5115 params[1] = (gpointer)trps;
5116 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5118 /* get a list of adjacent-in-time tracks */
5119 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5122 GList *l = nearby_tracks;
5125 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5126 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5128 t1 = get_first_trackpoint(l)->timestamp;
5129 t2 = get_last_trackpoint(l)->timestamp;
5130 #undef get_first_trackpoint
5131 #undef get_last_trackpoint
5132 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5135 /* remove trackpoints from merged track, delete track */
5136 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5137 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5139 // Tracks have changed, therefore retry again against all the remaining tracks
5140 attempt_merge = TRUE;
5145 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5148 g_list_free(nearby_tracks);
5150 vik_layer_emit_update( VIK_LAYER(vtl) );
5154 * Split a track at the currently selected trackpoint
5156 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5158 if ( !vtl->current_tpl )
5161 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5162 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5164 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5165 GList *newglist = g_list_alloc ();
5166 newglist->prev = NULL;
5167 newglist->next = vtl->current_tpl->next;
5168 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5169 tr->trackpoints = newglist;
5171 vtl->current_tpl->next->prev = newglist; /* end old track here */
5172 vtl->current_tpl->next = NULL;
5174 // Bounds of the selected track changed due to the split
5175 vik_track_calculate_bounds ( vtl->current_tp_track );
5177 vtl->current_tpl = newglist; /* change tp to first of new track. */
5178 vtl->current_tp_track = tr;
5181 vik_trw_layer_add_route ( vtl, name, tr );
5183 vik_trw_layer_add_track ( vtl, name, tr );
5185 // Bounds of the new track created by the split
5186 vik_track_calculate_bounds ( tr );
5192 // Also need id of newly created track
5195 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5197 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5199 if ( trkf && udata.uuid )
5200 vtl->current_tp_id = udata.uuid;
5202 vtl->current_tp_id = NULL;
5204 vik_layer_emit_update(VIK_LAYER(vtl));
5209 /* split by time routine */
5210 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5212 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5213 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5214 GList *trps = track->trackpoints;
5216 GList *newlists = NULL;
5217 GList *newtps = NULL;
5218 static guint thr = 1;
5225 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5226 _("Split Threshold..."),
5227 _("Split when time between trackpoints exceeds:"),
5232 /* iterate through trackpoints, and copy them into new lists without touching original list */
5233 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5237 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5239 g_print("panic: ts < prev_ts: this should never happen!\n");
5242 if (ts - prev_ts > thr*60) {
5243 /* flush accumulated trackpoints into new list */
5244 newlists = g_list_append(newlists, g_list_reverse(newtps));
5248 /* accumulate trackpoint copies in newtps, in reverse order */
5249 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5251 iter = g_list_next(iter);
5254 newlists = g_list_append(newlists, g_list_reverse(newtps));
5257 /* put lists of trackpoints into tracks */
5259 // Only bother updating if the split results in new tracks
5260 if (g_list_length (newlists) > 1) {
5265 tr = vik_track_copy ( track, FALSE );
5266 tr->trackpoints = (GList *)(iter->data);
5268 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5269 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5270 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5271 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5272 vik_track_calculate_bounds ( tr );
5274 iter = g_list_next(iter);
5276 // Remove original track and then update the display
5277 vik_trw_layer_delete_track (vtl, track);
5278 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5280 g_list_free(newlists);
5284 * Split a track by the number of points as specified by the user
5286 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5288 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5290 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5291 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5293 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5298 // Check valid track
5299 GList *trps = track->trackpoints;
5303 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5304 _("Split Every Nth Point"),
5305 _("Split on every Nth point:"),
5306 250, // Default value as per typical limited track capacity of various GPS devices
5310 // Was a valid number returned?
5316 GList *newlists = NULL;
5317 GList *newtps = NULL;
5322 /* accumulate trackpoint copies in newtps, in reverse order */
5323 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5325 if (count >= points) {
5326 /* flush accumulated trackpoints into new list */
5327 newlists = g_list_append(newlists, g_list_reverse(newtps));
5331 iter = g_list_next(iter);
5334 // If there is a remaining chunk put that into the new split list
5335 // This may well be the whole track if no split points were encountered
5337 newlists = g_list_append(newlists, g_list_reverse(newtps));
5340 /* put lists of trackpoints into tracks */
5342 // Only bother updating if the split results in new tracks
5343 if (g_list_length (newlists) > 1) {
5348 tr = vik_track_copy ( track, FALSE );
5349 tr->trackpoints = (GList *)(iter->data);
5351 if ( track->is_route ) {
5352 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5353 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5356 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5357 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5359 vik_track_calculate_bounds ( tr );
5361 iter = g_list_next(iter);
5363 // Remove original track and then update the display
5364 if ( track->is_route )
5365 vik_trw_layer_delete_route (vtl, track);
5367 vik_trw_layer_delete_track (vtl, track);
5368 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5370 g_list_free(newlists);
5374 * Split a track at the currently selected trackpoint
5376 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5378 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5379 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5380 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5384 * Split a track by its segments
5385 * Routes do not have segments so don't call this for routes
5387 static void trw_layer_split_segments ( gpointer pass_along[6] )
5389 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5390 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5397 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5400 for ( i = 0; i < ntracks; i++ ) {
5402 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5403 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5408 // Remove original track
5409 vik_trw_layer_delete_track ( vtl, trk );
5410 vik_layer_emit_update ( VIK_LAYER(vtl) );
5413 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5416 /* end of split/merge routines */
5419 * Delete adjacent track points at the same position
5420 * AKA Delete Dulplicates on the Properties Window
5422 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5424 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5426 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5427 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5429 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5434 gulong removed = vik_track_remove_dup_points ( trk );
5436 // Track has been updated so update tps:
5437 trw_layer_cancel_tps_of_track ( vtl, trk );
5439 // Inform user how much was deleted as it's not obvious from the normal view
5441 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5442 g_snprintf(str, 64, tmp_str, removed);
5443 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5445 vik_layer_emit_update ( VIK_LAYER(vtl) );
5449 * Delete adjacent track points with the same timestamp
5450 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5452 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5454 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5456 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5457 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5459 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5464 gulong removed = vik_track_remove_same_time_points ( trk );
5466 // Track has been updated so update tps:
5467 trw_layer_cancel_tps_of_track ( vtl, trk );
5469 // Inform user how much was deleted as it's not obvious from the normal view
5471 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5472 g_snprintf(str, 64, tmp_str, removed);
5473 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5475 vik_layer_emit_update ( VIK_LAYER(vtl) );
5481 static void trw_layer_reverse ( gpointer pass_along[6] )
5483 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5485 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5486 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5488 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5493 vik_track_reverse ( track );
5495 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5499 * Similar to trw_layer_enum_item, but this uses a sorted method
5502 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5504 GList **list = (GList**)udata;
5505 // *list = g_list_prepend(*all, key); //unsorted method
5506 // Sort named list alphabetically
5507 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5512 * Now Waypoint specific sort
5514 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5516 GList **list = (GList**)udata;
5517 // Sort named list alphabetically
5518 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5522 * Track specific sort
5524 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5526 GList **list = (GList**)udata;
5527 // Sort named list alphabetically
5528 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5533 gboolean has_same_track_name;
5534 const gchar *same_track_name;
5535 } same_track_name_udata;
5537 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5539 const gchar* namea = (const gchar*) aa;
5540 const gchar* nameb = (const gchar*) bb;
5543 gint result = strcmp ( namea, nameb );
5545 if ( result == 0 ) {
5546 // Found two names the same
5547 same_track_name_udata *user_data = udata;
5548 user_data->has_same_track_name = TRUE;
5549 user_data->same_track_name = namea;
5552 // Leave ordering the same
5557 * Find out if any tracks have the same name in this hash table
5559 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5561 // Sort items by name, then compare if any next to each other are the same
5563 GList *track_names = NULL;
5564 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5567 if ( ! track_names )
5570 same_track_name_udata udata;
5571 udata.has_same_track_name = FALSE;
5573 // Use sort routine to traverse list comparing items
5574 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5575 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5576 // Still no tracks...
5580 return udata.has_same_track_name;
5584 * Force unqiue track names for the track table specified
5585 * Note the panel is a required parameter to enable the update of the names displayed
5586 * Specify if on tracks or else on routes
5588 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5590 // . Search list for an instance of repeated name
5591 // . get track of this name
5592 // . create new name
5593 // . rename track & update equiv. treeview iter
5594 // . repeat until all different
5596 same_track_name_udata udata;
5598 GList *track_names = NULL;
5599 udata.has_same_track_name = FALSE;
5600 udata.same_track_name = NULL;
5602 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5605 if ( ! track_names )
5608 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5610 // Still no tracks...
5611 if ( ! dummy_list1 )
5614 while ( udata.has_same_track_name ) {
5616 // Find a track with the same name
5619 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5621 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5625 g_critical("Houston, we've had a problem.");
5626 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5627 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5632 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5633 vik_track_set_name ( trk, newname );
5639 // Need want key of it for treeview update
5640 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5642 if ( trkf && udataU.uuid ) {
5646 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5648 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5651 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5652 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5653 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5658 // Start trying to find same names again...
5660 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5661 udata.has_same_track_name = FALSE;
5662 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5664 // No tracks any more - give up searching
5665 if ( ! dummy_list2 )
5666 udata.has_same_track_name = FALSE;
5670 vik_layers_panel_emit_update ( vlp );
5676 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5678 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5681 // Ensure list of track names offered is unique
5682 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5683 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5684 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5685 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5691 // Sort list alphabetically for better presentation
5692 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5695 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5699 // Get list of items to delete from the user
5700 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5703 _("Delete Selection"),
5704 _("Select tracks to delete"));
5707 // Delete requested tracks
5708 // since specificly requested, IMHO no need for extra confirmation
5709 if ( delete_list ) {
5711 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5712 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5713 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5715 g_list_free(delete_list);
5716 vik_layer_emit_update( VIK_LAYER(vtl) );
5723 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5725 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5728 // Ensure list of track names offered is unique
5729 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5730 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5731 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5732 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5738 // Sort list alphabetically for better presentation
5739 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5742 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5746 // Get list of items to delete from the user
5747 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5750 _("Delete Selection"),
5751 _("Select routes to delete") );
5754 // Delete requested routes
5755 // since specificly requested, IMHO no need for extra confirmation
5756 if ( delete_list ) {
5758 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5759 // This deletes first route it finds of that name (but uniqueness is enforced above)
5760 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5762 g_list_free(delete_list);
5763 vik_layer_emit_update( VIK_LAYER(vtl) );
5768 gboolean has_same_waypoint_name;
5769 const gchar *same_waypoint_name;
5770 } same_waypoint_name_udata;
5772 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5774 const gchar* namea = (const gchar*) aa;
5775 const gchar* nameb = (const gchar*) bb;
5778 gint result = strcmp ( namea, nameb );
5780 if ( result == 0 ) {
5781 // Found two names the same
5782 same_waypoint_name_udata *user_data = udata;
5783 user_data->has_same_waypoint_name = TRUE;
5784 user_data->same_waypoint_name = namea;
5787 // Leave ordering the same
5792 * Find out if any waypoints have the same name in this layer
5794 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5796 // Sort items by name, then compare if any next to each other are the same
5798 GList *waypoint_names = NULL;
5799 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5802 if ( ! waypoint_names )
5805 same_waypoint_name_udata udata;
5806 udata.has_same_waypoint_name = FALSE;
5808 // Use sort routine to traverse list comparing items
5809 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5810 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5811 // Still no waypoints...
5815 return udata.has_same_waypoint_name;
5819 * Force unqiue waypoint names for this layer
5820 * Note the panel is a required parameter to enable the update of the names displayed
5822 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5824 // . Search list for an instance of repeated name
5825 // . get waypoint of this name
5826 // . create new name
5827 // . rename waypoint & update equiv. treeview iter
5828 // . repeat until all different
5830 same_waypoint_name_udata udata;
5832 GList *waypoint_names = NULL;
5833 udata.has_same_waypoint_name = FALSE;
5834 udata.same_waypoint_name = NULL;
5836 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5839 if ( ! waypoint_names )
5842 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5844 // Still no waypoints...
5845 if ( ! dummy_list1 )
5848 while ( udata.has_same_waypoint_name ) {
5850 // Find a waypoint with the same name
5851 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5855 g_critical("Houston, we've had a problem.");
5856 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5857 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5862 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5863 vik_waypoint_set_name ( waypoint, newname );
5866 udataU.wp = waypoint;
5869 // Need want key of it for treeview update
5870 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5872 if ( wpf && udataU.uuid ) {
5874 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5877 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5878 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5879 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5884 // Start trying to find same names again...
5885 waypoint_names = NULL;
5886 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5887 udata.has_same_waypoint_name = FALSE;
5888 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5890 // No waypoints any more - give up searching
5891 if ( ! dummy_list2 )
5892 udata.has_same_waypoint_name = FALSE;
5896 vik_layers_panel_emit_update ( vlp );
5902 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5904 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5907 // Ensure list of waypoint names offered is unique
5908 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5909 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5910 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5911 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5917 // Sort list alphabetically for better presentation
5918 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5920 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5924 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5926 // Get list of items to delete from the user
5927 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5930 _("Delete Selection"),
5931 _("Select waypoints to delete"));
5934 // Delete requested waypoints
5935 // since specificly requested, IMHO no need for extra confirmation
5936 if ( delete_list ) {
5938 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5939 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5940 trw_layer_delete_waypoint_by_name (vtl, l->data);
5942 g_list_free(delete_list);
5944 trw_layer_calculate_bounds_waypoints ( vtl );
5945 vik_layer_emit_update( VIK_LAYER(vtl) );
5950 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5952 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5954 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5957 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5959 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5962 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
5963 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5967 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
5969 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5972 if ( !strncmp(wp->comment, "http", 4) ) {
5973 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
5974 } else if ( !strncmp(wp->description, "http", 4) ) {
5975 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
5979 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
5981 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5983 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
5985 // No actual change to the name supplied
5987 if (strcmp(newname, wp->name) == 0 )
5990 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5993 // An existing waypoint has been found with the requested name
5994 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5995 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6000 // Update WP name and refresh the treeview
6001 vik_waypoint_set_name (wp, newname);
6003 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6004 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6007 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6012 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6014 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6016 // No actual change to the name supplied
6018 if (strcmp(newname, trk->name) == 0)
6021 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6024 // An existing track has been found with the requested name
6025 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6026 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6030 // Update track name and refresh GUI parts
6031 vik_track_set_name (trk, newname);
6033 // Update any subwindows that could be displaying this track which has changed name
6034 // Only one Track Edit Window
6035 if ( l->current_tp_track == trk && l->tpwin ) {
6036 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6038 // Property Dialog of the track
6039 vik_trw_layer_propwin_update ( trk );
6041 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6042 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6045 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6050 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6052 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6054 // No actual change to the name supplied
6056 if (strcmp(newname, trk->name) == 0)
6059 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6062 // An existing track has been found with the requested name
6063 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6064 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6068 // Update track name and refresh GUI parts
6069 vik_track_set_name (trk, newname);
6071 // Update any subwindows that could be displaying this track which has changed name
6072 // Only one Track Edit Window
6073 if ( l->current_tp_track == trk && l->tpwin ) {
6074 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6076 // Property Dialog of the track
6077 vik_trw_layer_propwin_update ( trk );
6079 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6080 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6083 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6090 static gboolean is_valid_geocache_name ( gchar *str )
6092 gint len = strlen ( str );
6093 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]));
6096 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6098 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6099 a_acquire_set_filter_track ( trk );
6102 #ifdef VIK_CONFIG_GOOGLE
6103 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6105 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6106 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6109 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6111 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6113 gchar *escaped = uri_escape ( tr->comment );
6114 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6115 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6122 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6123 /* viewpoint is now available instead */
6124 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6126 static gpointer pass_along[8];
6128 gboolean rv = FALSE;
6131 pass_along[1] = vlp;
6132 pass_along[2] = GINT_TO_POINTER (subtype);
6133 pass_along[3] = sublayer;
6134 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6135 pass_along[5] = vvp;
6136 pass_along[6] = iter;
6137 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6139 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6143 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6145 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6146 gtk_widget_show ( item );
6148 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6149 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6150 if (tr && tr->property_dialog)
6151 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6153 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6154 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6155 if (tr && tr->property_dialog)
6156 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6159 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6161 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6162 gtk_widget_show ( item );
6164 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6165 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6166 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6167 gtk_widget_show ( item );
6169 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6171 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6172 gtk_widget_show ( item );
6174 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6176 gboolean separator_created = FALSE;
6178 /* could be a right-click using the tool */
6179 if ( vlp != NULL ) {
6180 item = gtk_menu_item_new ();
6181 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6182 gtk_widget_show ( item );
6184 separator_created = TRUE;
6186 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6187 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6189 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6190 gtk_widget_show ( item );
6193 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6195 if ( wp && wp->name ) {
6196 if ( is_valid_geocache_name ( wp->name ) ) {
6198 if ( !separator_created ) {
6199 item = gtk_menu_item_new ();
6200 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6201 gtk_widget_show ( item );
6202 separator_created = TRUE;
6205 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6207 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6208 gtk_widget_show ( item );
6212 if ( wp && wp->image )
6214 if ( !separator_created ) {
6215 item = gtk_menu_item_new ();
6216 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6217 gtk_widget_show ( item );
6218 separator_created = TRUE;
6221 // Set up image paramater
6222 pass_along[5] = wp->image;
6224 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6225 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
6226 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6227 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6228 gtk_widget_show ( item );
6230 #ifdef VIK_CONFIG_GEOTAG
6231 GtkWidget *geotag_submenu = gtk_menu_new ();
6232 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6233 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6234 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6235 gtk_widget_show ( item );
6236 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6238 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6239 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6240 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6241 gtk_widget_show ( item );
6243 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6244 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6245 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6246 gtk_widget_show ( item );
6252 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6253 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6254 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6255 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6256 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6257 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6258 gtk_widget_show ( item );
6265 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6266 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6267 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6268 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6269 gtk_widget_show ( item );
6270 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6271 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6272 gtk_widget_set_sensitive ( item, TRUE );
6274 gtk_widget_set_sensitive ( item, FALSE );
6277 item = gtk_menu_item_new ();
6278 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6279 gtk_widget_show ( item );
6282 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6285 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6286 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6287 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6288 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6289 gtk_widget_show ( item );
6292 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6294 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6295 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6297 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6298 gtk_widget_show ( item );
6300 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6301 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6302 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6303 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6304 gtk_widget_show ( item );
6306 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6307 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6308 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6309 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6310 gtk_widget_show ( item );
6312 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6313 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6314 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6315 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6316 gtk_widget_show ( item );
6319 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6323 if ( l->current_track && !l->current_track->is_route ) {
6324 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6325 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6326 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6327 gtk_widget_show ( item );
6329 item = gtk_menu_item_new ();
6330 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6331 gtk_widget_show ( item );
6334 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
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_tracks_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 ( _("_New Track") );
6341 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6343 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6344 gtk_widget_show ( item );
6345 // Make it available only when a new track *not* already in progress
6346 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6348 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6349 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6350 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6351 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6352 gtk_widget_show ( item );
6354 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6355 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6356 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6357 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6358 gtk_widget_show ( item );
6361 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6365 if ( l->current_track && l->current_track->is_route ) {
6366 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6367 // Reuse finish track method
6368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6369 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6370 gtk_widget_show ( item );
6372 item = gtk_menu_item_new ();
6373 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6374 gtk_widget_show ( item );
6377 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6378 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6380 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6381 gtk_widget_show ( item );
6383 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6384 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6386 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6387 gtk_widget_show ( item );
6388 // Make it available only when a new track *not* already in progress
6389 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6391 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6392 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6394 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6395 gtk_widget_show ( item );
6397 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6398 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6399 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6400 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6401 gtk_widget_show ( item );
6404 GtkWidget *upload_submenu = gtk_menu_new ();
6406 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6408 item = gtk_menu_item_new ();
6409 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6410 gtk_widget_show ( item );
6412 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6413 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6414 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6415 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6416 if ( l->current_track ) {
6417 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6418 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6419 gtk_widget_show ( item );
6422 item = gtk_menu_item_new ();
6423 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6424 gtk_widget_show ( item );
6427 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6428 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6430 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6431 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6432 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6433 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6434 gtk_widget_show ( item );
6436 GtkWidget *goto_submenu;
6437 goto_submenu = gtk_menu_new ();
6438 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6439 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6440 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6441 gtk_widget_show ( item );
6442 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6444 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6445 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6446 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6447 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6448 gtk_widget_show ( item );
6450 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6451 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6452 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6453 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6454 gtk_widget_show ( item );
6456 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6457 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6458 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6459 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6460 gtk_widget_show ( item );
6462 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6463 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6464 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6465 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6466 gtk_widget_show ( item );
6468 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6469 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6470 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6471 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6472 gtk_widget_show ( item );
6474 // Routes don't have speeds
6475 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6476 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6477 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6478 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6479 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6480 gtk_widget_show ( item );
6483 GtkWidget *combine_submenu;
6484 combine_submenu = gtk_menu_new ();
6485 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6486 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6487 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6488 gtk_widget_show ( item );
6489 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6491 // Routes don't have times or segments...
6492 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6493 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6495 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6496 gtk_widget_show ( item );
6498 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6499 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6500 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6501 gtk_widget_show ( item );
6504 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6505 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6506 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6507 gtk_widget_show ( item );
6509 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6510 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6512 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6513 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6514 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6515 gtk_widget_show ( item );
6517 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6518 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6520 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6521 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6522 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6523 gtk_widget_show ( item );
6525 GtkWidget *split_submenu;
6526 split_submenu = gtk_menu_new ();
6527 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6528 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6529 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6530 gtk_widget_show ( item );
6531 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6533 // Routes don't have times or segments...
6534 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6535 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6536 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6537 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6538 gtk_widget_show ( item );
6540 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6541 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6542 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6543 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6544 gtk_widget_show ( item );
6547 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6548 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6549 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6550 gtk_widget_show ( item );
6552 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6553 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6554 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6555 gtk_widget_show ( item );
6556 // Make it available only when a trackpoint is selected.
6557 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6559 GtkWidget *delete_submenu;
6560 delete_submenu = gtk_menu_new ();
6561 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6562 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6563 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6564 gtk_widget_show ( item );
6565 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6567 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6568 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6569 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6570 gtk_widget_show ( item );
6572 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6573 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6574 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6575 gtk_widget_show ( item );
6577 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6578 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6580 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6581 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6582 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6583 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6584 gtk_widget_show ( item );
6586 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6588 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6589 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6591 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6592 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
6593 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6594 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6595 gtk_widget_show ( item );
6598 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6599 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
6600 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6601 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6602 gtk_widget_show ( item );
6604 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6605 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6607 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6608 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6609 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6610 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6611 gtk_widget_show ( item );
6613 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6614 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6616 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6617 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6618 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6619 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6620 gtk_widget_show ( item );
6622 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6623 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6625 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6626 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6627 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6628 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6629 gtk_widget_show ( item );
6631 #ifdef VIK_CONFIG_GOOGLE
6632 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6633 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6634 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
6635 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6636 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6637 gtk_widget_show ( item );
6641 // ATM can't upload a single waypoint but can do waypoints to a GPS
6642 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6643 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6644 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6645 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6646 gtk_widget_show ( item );
6647 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6649 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6650 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6651 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6652 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6653 gtk_widget_show ( item );
6657 #ifdef VIK_CONFIG_GOOGLE
6658 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6660 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6661 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6662 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6663 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6664 gtk_widget_show ( item );
6668 // Some things aren't usable with routes
6669 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6670 #ifdef VIK_CONFIG_OPENSTREETMAP
6671 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6672 // Convert internal pointer into actual track for usage outside this file
6673 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6674 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6675 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6676 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6677 gtk_widget_show ( item );
6680 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6681 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6682 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6683 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6684 gtk_widget_show ( item );
6686 /* ATM This function is only available via the layers panel, due to needing a vlp */
6688 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6689 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6690 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6692 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6693 gtk_widget_show ( item );
6697 #ifdef VIK_CONFIG_GEOTAG
6698 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6700 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6701 gtk_widget_show ( item );
6705 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6706 // Only show on viewport popmenu when a trackpoint is selected
6707 if ( ! vlp && l->current_tpl ) {
6709 item = gtk_menu_item_new ();
6710 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6711 gtk_widget_show ( item );
6713 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6716 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6717 gtk_widget_show ( item );
6724 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6727 if (!vtl->current_tpl)
6729 if (!vtl->current_tpl->next)
6732 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6733 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6735 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6738 VikTrackpoint *tp_new = vik_trackpoint_new();
6739 struct LatLon ll_current, ll_next;
6740 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6741 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6743 /* main positional interpolation */
6744 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6745 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6747 /* Now other properties that can be interpolated */
6748 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6750 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6751 /* Note here the division is applied to each part, then added
6752 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6753 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6754 tp_new->has_timestamp = TRUE;
6757 if (tp_current->speed != NAN && tp_next->speed != NAN)
6758 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6760 /* TODO - improve interpolation of course, as it may not be correct.
6761 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6762 [similar applies if value is in radians] */
6763 if (tp_current->course != NAN && tp_next->course != NAN)
6764 tp_new->speed = (tp_current->course + tp_next->course)/2;
6766 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6768 /* Insert new point into the trackpoints list after the current TP */
6769 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6771 // Otherwise try routes
6772 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6776 gint index = g_list_index ( trk->trackpoints, tp_current );
6778 // NB no recalculation of bounds since it is inserted between points
6779 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6784 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6790 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6794 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6796 if ( vtl->current_tpl )
6798 vtl->current_tpl = NULL;
6799 vtl->current_tp_track = NULL;
6800 vtl->current_tp_id = NULL;
6801 vik_layer_emit_update(VIK_LAYER(vtl));
6805 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6807 g_assert ( vtl->tpwin != NULL );
6808 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6809 trw_layer_cancel_current_tp ( vtl, TRUE );
6811 if ( vtl->current_tpl == NULL )
6814 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6816 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6817 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6819 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6821 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6823 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6829 // Find available adjacent trackpoint
6830 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6832 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6833 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6835 // Delete current trackpoint
6836 vik_trackpoint_free ( vtl->current_tpl->data );
6837 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6839 // Set to current to the available adjacent trackpoint
6840 vtl->current_tpl = new_tpl;
6842 // Reset dialog with the available adjacent trackpoint
6843 if ( vtl->current_tp_track ) {
6844 vik_track_calculate_bounds ( vtl->current_tp_track );
6845 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6848 vik_layer_emit_update(VIK_LAYER(vtl));
6852 // Delete current trackpoint
6853 vik_trackpoint_free ( vtl->current_tpl->data );
6854 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6855 trw_layer_cancel_current_tp ( vtl, FALSE );
6858 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6860 if ( vtl->current_tp_track )
6861 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6862 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
6864 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6866 if ( vtl->current_tp_track )
6867 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6868 vik_layer_emit_update(VIK_LAYER(vtl));
6870 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6872 trw_layer_insert_tp_after_current_tp ( vtl );
6873 vik_layer_emit_update(VIK_LAYER(vtl));
6875 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6876 vik_layer_emit_update(VIK_LAYER(vtl));
6879 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
6883 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6884 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
6885 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
6886 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
6887 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
6889 if ( vtl->current_tpl )
6890 if ( vtl->current_tp_track )
6891 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6892 /* set layer name and TP data */
6895 /***************************************************************************
6897 ***************************************************************************/
6899 /*** Utility data structures and functions ****/
6903 gint closest_x, closest_y;
6904 gpointer *closest_wp_id;
6905 VikWaypoint *closest_wp;
6911 gint closest_x, closest_y;
6912 gpointer closest_track_id;
6913 VikTrackpoint *closest_tp;
6919 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
6925 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
6927 // If waypoint has an image then use the image size to select
6929 gint slackx, slacky;
6930 slackx = wp->image_width / 2;
6931 slacky = wp->image_height / 2;
6933 if ( x <= params->x + slackx && x >= params->x - slackx
6934 && y <= params->y + slacky && y >= params->y - slacky ) {
6935 params->closest_wp_id = id;
6936 params->closest_wp = wp;
6937 params->closest_x = x;
6938 params->closest_y = y;
6941 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
6942 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
6943 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6945 params->closest_wp_id = id;
6946 params->closest_wp = wp;
6947 params->closest_x = x;
6948 params->closest_y = y;
6952 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
6954 GList *tpl = t->trackpoints;
6960 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
6966 tp = VIK_TRACKPOINT(tpl->data);
6968 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
6970 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
6971 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
6972 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6974 params->closest_track_id = id;
6975 params->closest_tp = tp;
6976 params->closest_tpl = tpl;
6977 params->closest_x = x;
6978 params->closest_y = y;
6984 // ATM: Leave this as 'Track' only.
6985 // Not overly bothered about having a snap to route trackpoint capability
6986 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6988 TPSearchParams params;
6992 params.closest_track_id = NULL;
6993 params.closest_tp = NULL;
6994 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
6995 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6996 return params.closest_tp;
6999 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7001 WPSearchParams params;
7005 params.closest_wp = NULL;
7006 params.closest_wp_id = NULL;
7007 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7008 return params.closest_wp;
7012 // Some forward declarations
7013 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7014 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7015 static void marker_end_move ( tool_ed_t *t );
7018 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7022 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7024 // Here always allow snapping back to the original location
7025 // this is useful when one decides not to move the thing afterall
7026 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7029 if ( event->state & GDK_CONTROL_MASK )
7031 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7033 new_coord = tp->coord;
7037 if ( event->state & GDK_SHIFT_MASK )
7039 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7041 new_coord = wp->coord;
7045 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7047 marker_moveto ( t, x, y );
7054 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7056 if ( t->holding && event->button == 1 )
7059 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7062 if ( event->state & GDK_CONTROL_MASK )
7064 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7066 new_coord = tp->coord;
7070 if ( event->state & GDK_SHIFT_MASK )
7072 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7074 new_coord = wp->coord;
7077 marker_end_move ( t );
7079 // Determine if working on a waypoint or a trackpoint
7080 if ( t->is_waypoint ) {
7081 vtl->current_wp->coord = new_coord;
7082 trw_layer_calculate_bounds_waypoints ( vtl );
7085 if ( vtl->current_tpl ) {
7086 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7088 if ( vtl->current_tp_track )
7089 vik_track_calculate_bounds ( vtl->current_tp_track );
7092 if ( vtl->current_tp_track )
7093 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7098 vtl->current_wp = NULL;
7099 vtl->current_wp_id = NULL;
7100 trw_layer_cancel_current_tp ( vtl, FALSE );
7102 vik_layer_emit_update ( VIK_LAYER(vtl) );
7109 Returns true if a waypoint or track is found near the requested event position for this particular layer
7110 The item found is automatically selected
7111 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7113 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7115 if ( event->button != 1 )
7118 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7121 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7125 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7127 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7129 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7130 WPSearchParams wp_params;
7131 wp_params.vvp = vvp;
7132 wp_params.x = event->x;
7133 wp_params.y = event->y;
7134 wp_params.closest_wp_id = NULL;
7135 wp_params.closest_wp = NULL;
7137 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7139 if ( wp_params.closest_wp ) {
7142 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7144 // Too easy to move it so must be holding shift to start immediately moving it
7145 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7146 if ( event->state & GDK_SHIFT_MASK ||
7147 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7148 // Put into 'move buffer'
7149 // NB vvp & vw already set in tet
7150 tet->vtl = (gpointer)vtl;
7151 tet->is_waypoint = TRUE;
7153 marker_begin_move (tet, event->x, event->y);
7156 vtl->current_wp = wp_params.closest_wp;
7157 vtl->current_wp_id = wp_params.closest_wp_id;
7159 vik_layer_emit_update ( VIK_LAYER(vtl) );
7165 // Used for both track and route lists
7166 TPSearchParams tp_params;
7167 tp_params.vvp = vvp;
7168 tp_params.x = event->x;
7169 tp_params.y = event->y;
7170 tp_params.closest_track_id = NULL;
7171 tp_params.closest_tp = NULL;
7172 tp_params.bbox = bbox;
7174 if (vtl->tracks_visible) {
7175 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7177 if ( tp_params.closest_tp ) {
7179 // Always select + highlight the track
7180 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7182 tet->is_waypoint = FALSE;
7184 // Select the Trackpoint
7185 // Can move it immediately when control held or it's the previously selected tp
7186 if ( event->state & GDK_CONTROL_MASK ||
7187 vtl->current_tpl == tp_params.closest_tpl ) {
7188 // Put into 'move buffer'
7189 // NB vvp & vw already set in tet
7190 tet->vtl = (gpointer)vtl;
7191 marker_begin_move (tet, event->x, event->y);
7194 vtl->current_tpl = tp_params.closest_tpl;
7195 vtl->current_tp_id = tp_params.closest_track_id;
7196 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7198 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7201 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7203 vik_layer_emit_update ( VIK_LAYER(vtl) );
7208 // Try again for routes
7209 if (vtl->routes_visible) {
7210 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7212 if ( tp_params.closest_tp ) {
7214 // Always select + highlight the track
7215 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7217 tet->is_waypoint = FALSE;
7219 // Select the Trackpoint
7220 // Can move it immediately when control held or it's the previously selected tp
7221 if ( event->state & GDK_CONTROL_MASK ||
7222 vtl->current_tpl == tp_params.closest_tpl ) {
7223 // Put into 'move buffer'
7224 // NB vvp & vw already set in tet
7225 tet->vtl = (gpointer)vtl;
7226 marker_begin_move (tet, event->x, event->y);
7229 vtl->current_tpl = tp_params.closest_tpl;
7230 vtl->current_tp_id = tp_params.closest_track_id;
7231 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7233 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7236 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7238 vik_layer_emit_update ( VIK_LAYER(vtl) );
7243 /* these aren't the droids you're looking for */
7244 vtl->current_wp = NULL;
7245 vtl->current_wp_id = NULL;
7246 trw_layer_cancel_current_tp ( vtl, FALSE );
7249 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7254 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7256 if ( event->button != 3 )
7259 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7262 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7265 /* Post menu for the currently selected item */
7267 /* See if a track is selected */
7268 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7269 if ( track && track->visible ) {
7271 if ( track->name ) {
7273 if ( vtl->track_right_click_menu )
7274 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
7276 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7283 if ( track->is_route )
7284 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7286 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7288 if ( trkf && udataU.uuid ) {
7291 if ( track->is_route )
7292 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7294 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7296 trw_layer_sublayer_add_menu_items ( vtl,
7297 vtl->track_right_click_menu,
7299 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7305 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7311 /* See if a waypoint is selected */
7312 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7313 if ( waypoint && waypoint->visible ) {
7314 if ( waypoint->name ) {
7316 if ( vtl->wp_right_click_menu )
7317 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7319 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7322 udata.wp = waypoint;
7325 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7327 if ( wpf && udata.uuid ) {
7328 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7330 trw_layer_sublayer_add_menu_items ( vtl,
7331 vtl->wp_right_click_menu,
7333 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7338 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7347 /* background drawing hook, to be passed the viewport */
7348 static gboolean tool_sync_done = TRUE;
7350 static gboolean tool_sync(gpointer data)
7352 VikViewport *vvp = data;
7353 gdk_threads_enter();
7354 vik_viewport_sync(vvp);
7355 tool_sync_done = TRUE;
7356 gdk_threads_leave();
7360 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7363 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7364 gdk_gc_set_function ( t->gc, GDK_INVERT );
7365 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7366 vik_viewport_sync(t->vvp);
7371 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7373 VikViewport *vvp = t->vvp;
7374 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7375 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7379 if (tool_sync_done) {
7380 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7381 tool_sync_done = FALSE;
7385 static void marker_end_move ( tool_ed_t *t )
7387 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7388 g_object_unref ( t->gc );
7392 /*** Edit waypoint ****/
7394 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7396 tool_ed_t *t = g_new(tool_ed_t, 1);
7402 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7404 WPSearchParams params;
7405 tool_ed_t *t = data;
7406 VikViewport *vvp = t->vvp;
7408 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7415 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7418 if ( vtl->current_wp && vtl->current_wp->visible )
7420 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7422 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7424 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7425 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7427 if ( event->button == 3 )
7428 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7430 marker_begin_move(t, event->x, event->y);
7437 params.x = event->x;
7438 params.y = event->y;
7439 params.closest_wp_id = NULL;
7440 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7441 params.closest_wp = NULL;
7442 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7443 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7445 // how do we get here?
7446 marker_begin_move(t, event->x, event->y);
7447 g_critical("shouldn't be here");
7450 else if ( params.closest_wp )
7452 if ( event->button == 3 )
7453 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7455 vtl->waypoint_rightclick = FALSE;
7457 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7459 vtl->current_wp = params.closest_wp;
7460 vtl->current_wp_id = params.closest_wp_id;
7462 /* could make it so don't update if old WP is off screen and new is null but oh well */
7463 vik_layer_emit_update ( VIK_LAYER(vtl) );
7467 vtl->current_wp = NULL;
7468 vtl->current_wp_id = NULL;
7469 vtl->waypoint_rightclick = FALSE;
7470 vik_layer_emit_update ( VIK_LAYER(vtl) );
7474 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7476 tool_ed_t *t = data;
7477 VikViewport *vvp = t->vvp;
7479 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7484 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7487 if ( event->state & GDK_CONTROL_MASK )
7489 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7491 new_coord = tp->coord;
7495 if ( event->state & GDK_SHIFT_MASK )
7497 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7498 if ( wp && wp != vtl->current_wp )
7499 new_coord = wp->coord;
7504 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7506 marker_moveto ( t, x, y );
7513 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7515 tool_ed_t *t = data;
7516 VikViewport *vvp = t->vvp;
7518 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7521 if ( t->holding && event->button == 1 )
7524 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7527 if ( event->state & GDK_CONTROL_MASK )
7529 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7531 new_coord = tp->coord;
7535 if ( event->state & GDK_SHIFT_MASK )
7537 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7538 if ( wp && wp != vtl->current_wp )
7539 new_coord = wp->coord;
7542 marker_end_move ( t );
7544 vtl->current_wp->coord = new_coord;
7546 trw_layer_calculate_bounds_waypoints ( vtl );
7547 vik_layer_emit_update ( VIK_LAYER(vtl) );
7550 /* PUT IN RIGHT PLACE!!! */
7551 if ( event->button == 3 && vtl->waypoint_rightclick )
7553 if ( vtl->wp_right_click_menu )
7554 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7555 if ( vtl->current_wp ) {
7556 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7557 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 );
7558 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7560 vtl->waypoint_rightclick = FALSE;
7565 /*** New track ****/
7567 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7574 GdkDrawable *drawable;
7580 * Draw specified pixmap
7582 static gboolean draw_sync ( gpointer data )
7584 draw_sync_t *ds = (draw_sync_t*) data;
7585 // Sometimes don't want to draw
7586 // normally because another update has taken precedent such as panning the display
7587 // which means this pixmap is no longer valid
7588 if ( ds->vtl->draw_sync_do ) {
7589 gdk_threads_enter();
7590 gdk_draw_drawable (ds->drawable,
7593 0, 0, 0, 0, -1, -1);
7594 ds->vtl->draw_sync_done = TRUE;
7595 gdk_threads_leave();
7600 static gchar* distance_string (gdouble distance)
7604 /* draw label with distance */
7605 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7606 switch (dist_units) {
7607 case VIK_UNITS_DISTANCE_MILES:
7608 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7609 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7610 } else if (distance < 1609.4) {
7611 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7613 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7617 // VIK_UNITS_DISTANCE_KILOMETRES
7618 if (distance >= 1000 && distance < 100000) {
7619 g_sprintf(str, "%3.2f km", distance/1000.0);
7620 } else if (distance < 1000) {
7621 g_sprintf(str, "%d m", (int)distance);
7623 g_sprintf(str, "%d km", (int)distance/1000);
7627 return g_strdup (str);
7631 * Actually set the message in statusbar
7633 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
7635 // Only show elevation data when track has some elevation properties
7636 gchar str_gain_loss[64];
7637 str_gain_loss[0] = '\0';
7638 gchar str_last_step[64];
7639 str_last_step[0] = '\0';
7640 gchar *str_total = distance_string (distance);
7642 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7643 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7644 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7646 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7649 if ( last_step > 0 ) {
7650 gchar *tmp = distance_string (last_step);
7651 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
7655 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
7657 // Write with full gain/loss information
7658 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
7659 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
7661 g_free ( str_total );
7665 * Figure out what information should be set in the statusbar and then write it
7667 static void update_statusbar ( VikTrwLayer *vtl )
7669 // Get elevation data
7670 gdouble elev_gain, elev_loss;
7671 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7673 /* Find out actual distance of current track */
7674 gdouble distance = vik_track_get_length (vtl->current_track);
7676 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
7680 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7682 /* if we haven't sync'ed yet, we don't have time to do more. */
7683 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7684 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7685 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7687 static GdkPixmap *pixmap = NULL;
7689 // Need to check in case window has been resized
7690 w1 = vik_viewport_get_width(vvp);
7691 h1 = vik_viewport_get_height(vvp);
7693 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7695 gdk_drawable_get_size (pixmap, &w2, &h2);
7696 if (w1 != w2 || h1 != h2) {
7697 g_object_unref ( G_OBJECT ( pixmap ) );
7698 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7701 // Reset to background
7702 gdk_draw_drawable (pixmap,
7703 vtl->current_track_newpoint_gc,
7704 vik_viewport_get_pixmap(vvp),
7705 0, 0, 0, 0, -1, -1);
7707 draw_sync_t *passalong;
7710 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7712 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7713 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7714 // thus when we come to reset to the background it would include what we have already drawn!!
7715 gdk_draw_line ( pixmap,
7716 vtl->current_track_newpoint_gc,
7717 x1, y1, event->x, event->y );
7718 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7720 /* Find out actual distance of current track */
7721 gdouble distance = vik_track_get_length (vtl->current_track);
7723 // Now add distance to where the pointer is //
7726 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7727 vik_coord_to_latlon ( &coord, &ll );
7728 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
7729 distance = distance + last_step;
7731 // Get elevation data
7732 gdouble elev_gain, elev_loss;
7733 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7735 // Adjust elevation data (if available) for the current pointer position
7737 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7738 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7739 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7740 // Adjust elevation of last track point
7741 if ( elev_new > last_tpt->altitude )
7743 elev_gain += elev_new - last_tpt->altitude;
7746 elev_loss += last_tpt->altitude - elev_new;
7750 gchar *str = distance_string (distance);
7752 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7753 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
7755 pango_layout_set_text (pl, str, -1);
7757 pango_layout_get_pixel_size ( pl, &wd, &hd );
7760 // offset from cursor a bit depending on font size
7764 // Create a background block to make the text easier to read over the background map
7765 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7766 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7767 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7769 g_object_unref ( G_OBJECT ( pl ) );
7770 g_object_unref ( G_OBJECT ( background_block_gc ) );
7772 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7773 passalong->vtl = vtl;
7774 passalong->pixmap = pixmap;
7775 passalong->drawable = GTK_WIDGET(vvp)->window;
7776 passalong->gc = vtl->current_track_newpoint_gc;
7780 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
7782 // Update statusbar with full gain/loss information
7783 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7787 // draw pixmap when we have time to
7788 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7789 vtl->draw_sync_done = FALSE;
7790 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7792 return VIK_LAYER_TOOL_ACK;
7795 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7797 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7798 vtl->current_track = NULL;
7799 vik_layer_emit_update ( VIK_LAYER(vtl) );
7801 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7803 if ( vtl->current_track->trackpoints )
7805 GList *last = g_list_last(vtl->current_track->trackpoints);
7806 g_free ( last->data );
7807 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7810 update_statusbar ( vtl );
7812 vik_layer_emit_update ( VIK_LAYER(vtl) );
7819 * Common function to handle trackpoint button requests on either a route or a track
7820 * . enables adding a point via normal click
7821 * . enables removal of last point via right click
7822 * . finishing of the track or route via double clicking
7824 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7828 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7831 if ( event->button == 2 ) {
7832 // As the display is panning, the new track pixmap is now invalid so don't draw it
7833 // otherwise this drawing done results in flickering back to an old image
7834 vtl->draw_sync_do = FALSE;
7838 if ( event->button == 3 )
7840 if ( !vtl->current_track )
7843 if ( vtl->current_track->trackpoints )
7845 GList *last = g_list_last(vtl->current_track->trackpoints);
7846 g_free ( last->data );
7847 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7849 vik_track_calculate_bounds ( vtl->current_track );
7850 update_statusbar ( vtl );
7852 vik_layer_emit_update ( VIK_LAYER(vtl) );
7856 if ( event->type == GDK_2BUTTON_PRESS )
7858 /* subtract last (duplicate from double click) tp then end */
7859 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7861 GList *last = g_list_last(vtl->current_track->trackpoints);
7862 g_free ( last->data );
7863 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7864 /* undo last, then end */
7865 vtl->current_track = NULL;
7867 vik_layer_emit_update ( VIK_LAYER(vtl) );
7871 tp = vik_trackpoint_new();
7872 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
7874 /* snap to other TP */
7875 if ( event->state & GDK_CONTROL_MASK )
7877 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7879 tp->coord = other_tp->coord;
7882 tp->newsegment = FALSE;
7883 tp->has_timestamp = FALSE;
7886 if ( vtl->current_track ) {
7887 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
7888 /* Auto attempt to get elevation from DEM data (if it's available) */
7889 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
7890 vik_track_calculate_bounds ( vtl->current_track );
7893 vtl->ct_x1 = vtl->ct_x2;
7894 vtl->ct_y1 = vtl->ct_y2;
7895 vtl->ct_x2 = event->x;
7896 vtl->ct_y2 = event->y;
7898 vik_layer_emit_update ( VIK_LAYER(vtl) );
7902 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7904 // ----------------------------------------------------- if current is a route - switch to new track
7905 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
7907 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
7908 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
7910 new_track_create_common ( vtl, name );
7915 return tool_new_track_or_route_click ( vtl, event, vvp );
7918 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7920 if ( event->button == 2 ) {
7921 // Pan moving ended - enable potential point drawing again
7922 vtl->draw_sync_do = TRUE;
7923 vtl->draw_sync_done = TRUE;
7927 /*** New route ****/
7929 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
7934 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7936 // -------------------------- if current is a track - switch to new route
7937 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
7939 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
7940 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
7941 new_route_create_common ( vtl, name );
7945 return tool_new_track_or_route_click ( vtl, event, vvp );
7948 /*** New waypoint ****/
7950 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7955 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7958 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7960 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
7961 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
7962 trw_layer_calculate_bounds_waypoints ( vtl );
7963 vik_layer_emit_update ( VIK_LAYER(vtl) );
7969 /*** Edit trackpoint ****/
7971 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
7973 tool_ed_t *t = g_new(tool_ed_t, 1);
7979 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7981 tool_ed_t *t = data;
7982 VikViewport *vvp = t->vvp;
7983 TPSearchParams params;
7984 /* OUTDATED DOCUMENTATION:
7985 find 5 pixel range on each side. then put these UTM, and a pointer
7986 to the winning track name (and maybe the winning track itself), and a
7987 pointer to the winning trackpoint, inside an array or struct. pass
7988 this along, do a foreach on the tracks which will do a foreach on the
7991 params.x = event->x;
7992 params.y = event->y;
7993 params.closest_track_id = NULL;
7994 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7995 params.closest_tp = NULL;
7997 if ( event->button != 1 )
8000 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8003 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8006 if ( vtl->current_tpl )
8008 /* 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.) */
8009 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8010 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8015 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8017 if ( current_tr->visible &&
8018 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8019 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8020 marker_begin_move ( t, event->x, event->y );
8026 if ( vtl->tracks_visible )
8027 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8029 if ( params.closest_tp )
8031 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8032 vtl->current_tpl = params.closest_tpl;
8033 vtl->current_tp_id = params.closest_track_id;
8034 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8035 trw_layer_tpwin_init ( vtl );
8036 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8037 vik_layer_emit_update ( VIK_LAYER(vtl) );
8041 if ( vtl->routes_visible )
8042 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8044 if ( params.closest_tp )
8046 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8047 vtl->current_tpl = params.closest_tpl;
8048 vtl->current_tp_id = params.closest_track_id;
8049 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8050 trw_layer_tpwin_init ( vtl );
8051 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8052 vik_layer_emit_update ( VIK_LAYER(vtl) );
8056 /* these aren't the droids you're looking for */
8060 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8062 tool_ed_t *t = data;
8063 VikViewport *vvp = t->vvp;
8065 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8071 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8074 if ( event->state & GDK_CONTROL_MASK )
8076 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8077 if ( tp && tp != vtl->current_tpl->data )
8078 new_coord = tp->coord;
8080 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8083 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8084 marker_moveto ( t, x, y );
8092 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8094 tool_ed_t *t = data;
8095 VikViewport *vvp = t->vvp;
8097 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8099 if ( event->button != 1)
8104 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8107 if ( event->state & GDK_CONTROL_MASK )
8109 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8110 if ( tp && tp != vtl->current_tpl->data )
8111 new_coord = tp->coord;
8114 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8115 if ( vtl->current_tp_track )
8116 vik_track_calculate_bounds ( vtl->current_tp_track );
8118 marker_end_move ( t );
8120 /* diff dist is diff from orig */
8122 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8124 vik_layer_emit_update ( VIK_LAYER(vtl) );
8131 #ifdef VIK_CONFIG_GOOGLE
8132 /*** Route Finder ***/
8133 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8138 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8141 if ( !vtl ) return FALSE;
8142 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8143 if ( event->button == 3 && vtl->route_finder_current_track ) {
8145 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8147 vtl->route_finder_coord = *new_end;
8149 vik_layer_emit_update ( VIK_LAYER(vtl) );
8150 /* remove last ' to:...' */
8151 if ( vtl->route_finder_current_track->comment ) {
8152 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8153 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8154 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8155 last_to - vtl->route_finder_current_track->comment - 1);
8156 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8161 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8162 struct LatLon start, end;
8163 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
8164 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
8167 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8168 vik_coord_to_latlon ( &(tmp), &end );
8169 vtl->route_finder_coord = tmp; /* for continuations */
8171 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8172 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8173 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8175 vtl->route_finder_check_added_track = TRUE;
8176 vtl->route_finder_started = FALSE;
8179 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
8180 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
8181 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
8182 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
8183 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
8184 // NB normally this returns a GPX Route - so subsequent usage of it must lookup via the routes hash
8185 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL, NULL );
8188 /* see if anything was done -- a track was added or appended to */
8189 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8190 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 ) );
8191 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8192 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8193 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8194 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8197 if ( vtl->route_finder_added_track )
8198 vik_track_calculate_bounds ( vtl->route_finder_added_track );
8200 vtl->route_finder_added_track = NULL;
8201 vtl->route_finder_check_added_track = FALSE;
8202 vtl->route_finder_append = FALSE;
8204 vik_layer_emit_update ( VIK_LAYER(vtl) );
8206 vtl->route_finder_started = TRUE;
8207 vtl->route_finder_coord = tmp;
8208 vtl->route_finder_current_track = NULL;
8214 /*** Show picture ****/
8216 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8221 /* Params are: vvp, event, last match found or NULL */
8222 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8224 if ( wp->image && wp->visible )
8226 gint x, y, slackx, slacky;
8227 GdkEventButton *event = (GdkEventButton *) params[1];
8229 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8230 slackx = wp->image_width / 2;
8231 slacky = wp->image_height / 2;
8232 if ( x <= event->x + slackx && x >= event->x - slackx
8233 && y <= event->y + slacky && y >= event->y - slacky )
8235 params[2] = wp->image; /* we've found a match. however continue searching
8236 * since we want to find the last match -- that
8237 * is, the match that was drawn last. */
8242 static void trw_layer_show_picture ( gpointer pass_along[6] )
8244 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8246 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8249 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8250 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8251 g_free ( quoted_file );
8252 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8254 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() );
8255 g_error_free ( err );
8258 #endif /* WINDOWS */
8261 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8263 gpointer params[3] = { vvp, event, NULL };
8264 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8266 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8269 static gpointer pass_along[6];
8270 pass_along[0] = vtl;
8271 pass_along[5] = params[2];
8272 trw_layer_show_picture ( pass_along );
8273 return TRUE; /* found a match */
8276 return FALSE; /* go through other layers, searching for a match */
8279 /***************************************************************************
8281 ***************************************************************************/
8284 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8286 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8287 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8290 /* Structure for thumbnail creating data used in the background thread */
8292 VikTrwLayer *vtl; // Layer needed for redrawing
8293 GSList *pics; // Image list
8294 } thumbnail_create_thread_data;
8296 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8298 guint total = g_slist_length(tctd->pics), done = 0;
8299 while ( tctd->pics )
8301 a_thumbnails_create ( (gchar *) tctd->pics->data );
8302 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8304 return -1; /* Abort thread */
8306 tctd->pics = tctd->pics->next;
8309 // Redraw to show the thumbnails as they are now created
8310 if ( IS_VIK_LAYER(tctd->vtl) )
8311 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8316 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8318 while ( tctd->pics )
8320 g_free ( tctd->pics->data );
8321 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8326 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8328 if ( ! vtl->has_verified_thumbnails )
8330 GSList *pics = NULL;
8331 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8334 gint len = g_slist_length ( pics );
8335 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8336 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8339 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8341 (vik_thr_func) create_thumbnails_thread,
8343 (vik_thr_free_func) thumbnail_create_thread_free,
8351 static const gchar* my_track_colors ( gint ii )
8353 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8365 // Fast and reliable way of returning a colour
8366 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8369 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8371 GHashTableIter iter;
8372 gpointer key, value;
8376 g_hash_table_iter_init ( &iter, vtl->tracks );
8378 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8380 // Tracks get a random spread of colours if not already assigned
8381 if ( ! VIK_TRACK(value)->has_color ) {
8382 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8383 VIK_TRACK(value)->color = vtl->track_color;
8385 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8387 VIK_TRACK(value)->has_color = TRUE;
8390 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8393 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8399 g_hash_table_iter_init ( &iter, vtl->routes );
8401 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8403 // Routes get an intermix of reds
8404 if ( ! VIK_TRACK(value)->has_color ) {
8406 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8408 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8409 VIK_TRACK(value)->has_color = TRUE;
8412 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8419 * (Re)Calculate the bounds of the waypoints in this layer,
8420 * This should be called whenever waypoints are changed
8422 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
8424 struct LatLon topleft = { 0.0, 0.0 };
8425 struct LatLon bottomright = { 0.0, 0.0 };
8428 GHashTableIter iter;
8429 gpointer key, value;
8431 g_hash_table_iter_init ( &iter, vtl->waypoints );
8433 // Set bounds to first point
8434 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
8435 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
8436 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
8439 // Ensure there is another point...
8440 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
8442 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8444 // See if this point increases the bounds.
8445 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
8447 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
8448 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
8449 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
8450 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
8454 vtl->waypoints_bbox.north = topleft.lat;
8455 vtl->waypoints_bbox.east = bottomright.lon;
8456 vtl->waypoints_bbox.south = bottomright.lat;
8457 vtl->waypoints_bbox.west = topleft.lon;
8460 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
8462 vik_track_calculate_bounds ( trk );
8465 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
8467 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8468 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8471 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
8473 trw_layer_verify_thumbnails ( vtl, vvp );
8474 trw_layer_track_alloc_colors ( vtl );
8476 trw_layer_calculate_bounds_waypoints ( vtl );
8477 trw_layer_calculate_bounds_tracks ( vtl );
8480 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8482 return vtl->coord_mode;
8486 * Uniquify the whole layer
8487 * Also requires the layers panel as the names shown there need updating too
8488 * Returns whether the operation was successful or not
8490 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8493 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8494 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8495 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8501 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8503 vik_coord_convert ( &(wp->coord), *dest_mode );
8506 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
8508 vik_track_convert ( tr, *dest_mode );
8511 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8513 if ( vtl->coord_mode != dest_mode )
8515 vtl->coord_mode = dest_mode;
8516 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8517 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8518 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
8522 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8524 vtl->menu_selection = selection;
8527 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8529 return (vtl->menu_selection);
8532 /* ----------- Downloading maps along tracks --------------- */
8534 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8536 /* TODO: calculating based on current size of viewport */
8537 const gdouble w_at_zoom_0_125 = 0.0013;
8538 const gdouble h_at_zoom_0_125 = 0.0011;
8539 gdouble zoom_factor = zoom_level/0.125;
8541 wh->lat = h_at_zoom_0_125 * zoom_factor;
8542 wh->lon = w_at_zoom_0_125 * zoom_factor;
8544 return 0; /* all OK */
8547 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8549 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8550 (dist->lat >= ABS(to->north_south - from->north_south)))
8553 VikCoord *coord = g_malloc(sizeof(VikCoord));
8554 coord->mode = VIK_COORD_LATLON;
8556 if (ABS(gradient) < 1) {
8557 if (from->east_west > to->east_west)
8558 coord->east_west = from->east_west - dist->lon;
8560 coord->east_west = from->east_west + dist->lon;
8561 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8563 if (from->north_south > to->north_south)
8564 coord->north_south = from->north_south - dist->lat;
8566 coord->north_south = from->north_south + dist->lat;
8567 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8573 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8575 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8576 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8578 VikCoord *next = from;
8580 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8582 list = g_list_prepend(list, next);
8588 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8590 typedef struct _Rect {
8595 #define GLRECT(iter) ((Rect *)((iter)->data))
8598 GList *rects_to_download = NULL;
8601 if (get_download_area_width(vvp, zoom_level, &wh))
8604 GList *iter = tr->trackpoints;
8608 gboolean new_map = TRUE;
8609 VikCoord *cur_coord, tl, br;
8612 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8614 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8615 rect = g_malloc(sizeof(Rect));
8618 rect->center = *cur_coord;
8619 rects_to_download = g_list_prepend(rects_to_download, rect);
8624 gboolean found = FALSE;
8625 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8626 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8637 GList *fillins = NULL;
8638 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8639 /* seems that ATM the function get_next_coord works only for LATLON */
8640 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8641 /* fill-ins for far apart points */
8642 GList *cur_rect, *next_rect;
8643 for (cur_rect = rects_to_download;
8644 (next_rect = cur_rect->next) != NULL;
8645 cur_rect = cur_rect->next) {
8646 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8647 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8648 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8652 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8655 GList *iter = fillins;
8657 cur_coord = (VikCoord *)(iter->data);
8658 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8659 rect = g_malloc(sizeof(Rect));
8662 rect->center = *cur_coord;
8663 rects_to_download = g_list_prepend(rects_to_download, rect);
8668 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8669 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8673 for (iter = fillins; iter; iter = iter->next)
8675 g_list_free(fillins);
8677 if (rects_to_download) {
8678 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8679 g_free(rect_iter->data);
8680 g_list_free(rects_to_download);
8684 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8687 gint selected_map, default_map;
8688 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8689 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8690 gint selected_zoom, default_zoom;
8694 VikTrwLayer *vtl = pass_along[0];
8695 VikLayersPanel *vlp = pass_along[1];
8697 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8698 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8700 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8704 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8706 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8707 int num_maps = g_list_length(vmls);
8710 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8714 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8715 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8717 gchar **np = map_names;
8718 VikMapsLayer **lp = map_layers;
8719 for (i = 0; i < num_maps; i++) {
8720 gboolean dup = FALSE;
8721 vml = (VikMapsLayer *)(vmls->data);
8722 for (j = 0; j < i; j++) { /* no duplicate allowed */
8723 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8730 *np++ = vik_maps_layer_get_map_label(vml);
8736 num_maps = lp - map_layers;
8738 for (default_map = 0; default_map < num_maps; default_map++) {
8739 /* TODO: check for parent layer's visibility */
8740 if (VIK_LAYER(map_layers[default_map])->visible)
8743 default_map = (default_map == num_maps) ? 0 : default_map;
8745 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8746 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8747 if (cur_zoom == zoom_vals[default_zoom])
8750 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8752 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8755 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8758 for (i = 0; i < num_maps; i++)
8759 g_free(map_names[i]);
8767 /**** lowest waypoint number calculation ***/
8768 static gint highest_wp_number_name_to_number(const gchar *name) {
8769 if ( strlen(name) == 3 ) {
8771 if ( n < 100 && name[0] != '0' )
8773 if ( n < 10 && name[0] != '0' )
8781 static void highest_wp_number_reset(VikTrwLayer *vtl)
8783 vtl->highest_wp_number = -1;
8786 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8788 /* if is bigger that top, add it */
8789 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8790 if ( new_wp_num > vtl->highest_wp_number )
8791 vtl->highest_wp_number = new_wp_num;
8794 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8796 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8797 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8798 if ( vtl->highest_wp_number == old_wp_num ) {
8800 vtl->highest_wp_number--;
8802 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8803 /* search down until we find something that *does* exist */
8805 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8806 vtl->highest_wp_number--;
8807 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8812 /* get lowest unused number */
8813 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8816 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8818 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8819 return g_strdup(buf);