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_get_window(GTK_WIDGET(vp)) == 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 gchar *cwd = g_get_current_dir();
2716 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
2720 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2722 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2724 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2725 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2727 gtk_widget_hide ( file_selector );
2728 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2729 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2730 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2735 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2737 gtk_widget_hide ( file_selector );
2738 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2739 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2740 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2745 gtk_widget_destroy ( file_selector );
2747 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2750 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2752 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2755 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2757 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2760 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2762 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2763 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2764 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2765 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2767 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2769 g_free ( auto_save_name );
2772 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2774 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2775 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2776 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2777 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2779 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2781 g_free ( auto_save_name );
2785 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2788 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2790 gchar *name_used = NULL;
2793 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2794 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2795 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2796 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2798 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2802 gchar *quoted_file = g_shell_quote ( name_used );
2803 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2804 g_free ( quoted_file );
2805 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2807 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2808 g_error_free ( err );
2812 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2813 //g_remove ( name_used );
2814 // Perhaps should be deleted when the program ends?
2815 // For now leave it to the user to delete it / use system temp cleanup methods.
2816 g_free ( name_used );
2820 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2822 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2825 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2827 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2830 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2832 gpointer layer_and_vlp[2];
2833 layer_and_vlp[0] = pass_along[0];
2834 layer_and_vlp[1] = pass_along[1];
2836 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2838 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2839 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2841 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2843 if ( !trk || !trk->name )
2846 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2847 gchar *auto_save_name = g_strdup ( trk->name );
2848 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2849 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2851 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2853 g_free ( auto_save_name );
2857 VikWaypoint *wp; // input
2858 gpointer uuid; // output
2861 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2863 wpu_udata *user_data = udata;
2864 if ( wp == user_data->wp ) {
2865 user_data->uuid = id;
2871 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2873 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2874 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2875 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2877 GTK_RESPONSE_REJECT,
2879 GTK_RESPONSE_ACCEPT,
2882 GtkWidget *label, *entry;
2883 label = gtk_label_new(_("Waypoint Name:"));
2884 entry = gtk_entry_new();
2886 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
2887 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
2888 gtk_widget_show_all ( label );
2889 gtk_widget_show_all ( entry );
2891 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2893 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2895 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2896 // Find *first* wp with the given name
2897 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2900 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2903 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2904 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2906 // Find and select on the side panel
2911 // Hmmm, want key of it
2912 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2914 if ( wpf && udata.uuid ) {
2915 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2916 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2925 gtk_widget_destroy ( dia );
2928 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2930 gchar *default_name = highest_wp_number_get(vtl);
2931 VikWaypoint *wp = vik_waypoint_new();
2932 gchar *returned_name;
2934 wp->coord = *def_coord;
2936 // Attempt to auto set height if DEM data is available
2937 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2938 if ( elev != VIK_DEM_INVALID_ELEVATION )
2939 wp->altitude = (gdouble)elev;
2941 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
2943 if ( returned_name )
2946 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2947 g_free (default_name);
2948 g_free (returned_name);
2951 g_free (default_name);
2952 vik_waypoint_free(wp);
2956 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2958 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2959 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2960 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2961 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2962 VikViewport *vvp = vik_window_viewport(vw);
2964 // Note the order is max part first then min part - thus reverse order of use in min_max function:
2965 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
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 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2973 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2974 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2975 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2977 trw_layer_find_maxmin (vtl, maxmin);
2978 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2979 trw_layer_calculate_bounds_waypoints ( vtl );
2980 vik_layers_panel_emit_update ( vlp );
2983 #ifdef VIK_CONFIG_GEOTAG
2984 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2986 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2988 // Update directly - not changing the mtime
2989 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2992 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2994 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2997 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3001 * Use code in separate file for this feature as reasonably complex
3003 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3005 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3006 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
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),
3016 static void trw_layer_geotagging ( gpointer lav[2] )
3018 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3019 // Unset so can be reverified later if necessary
3020 vtl->has_verified_thumbnails = FALSE;
3022 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3029 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3032 * Acquire into this TRW Layer straight from GPS Device
3034 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3036 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3037 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3038 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3039 VikViewport *vvp = vik_window_viewport(vw);
3041 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3042 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
3045 #ifdef VIK_CONFIG_GOOGLE
3047 * Acquire into this TRW Layer from Google Directions
3049 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
3051 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3052 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3053 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3054 VikViewport *vvp = vik_window_viewport(vw);
3056 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
3060 #ifdef VIK_CONFIG_OPENSTREETMAP
3062 * Acquire into this TRW Layer from OSM
3064 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3066 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3067 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3068 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3069 VikViewport *vvp = vik_window_viewport(vw);
3071 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
3075 * Acquire into this TRW Layer from OSM for 'My' Traces
3077 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3079 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3080 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3081 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3082 VikViewport *vvp = vik_window_viewport(vw);
3084 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface );
3088 #ifdef VIK_CONFIG_GEOCACHES
3090 * Acquire into this TRW Layer from Geocaching.com
3092 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3094 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3095 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3096 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3097 VikViewport *vvp = vik_window_viewport(vw);
3099 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
3103 #ifdef VIK_CONFIG_GEOTAG
3105 * Acquire into this TRW Layer from images
3107 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3109 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3110 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3111 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3112 VikViewport *vvp = vik_window_viewport(vw);
3114 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3115 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
3117 // Reverify thumbnails as they may have changed
3118 vtl->has_verified_thumbnails = FALSE;
3119 trw_layer_verify_thumbnails ( vtl, NULL );
3123 static void trw_layer_gps_upload ( gpointer lav[2] )
3125 gpointer pass_along[6];
3126 pass_along[0] = lav[0];
3127 pass_along[1] = lav[1];
3128 pass_along[2] = NULL; // No track - operate on the layer
3129 pass_along[3] = NULL;
3130 pass_along[4] = NULL;
3131 pass_along[5] = NULL;
3133 trw_layer_gps_upload_any ( pass_along );
3137 * If pass_along[3] is defined that this will upload just that track
3139 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3141 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3142 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3144 // May not actually get a track here as pass_along[2&3] can be null
3145 VikTrack *track = NULL;
3146 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3147 gboolean xfer_all = FALSE;
3149 if ( pass_along[2] ) {
3151 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3152 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3155 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3156 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3159 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3162 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3166 else if ( !pass_along[4] )
3167 xfer_all = TRUE; // i.e. whole layer
3169 if (track && !track->visible) {
3170 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3174 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3175 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3176 GTK_DIALOG_DESTROY_WITH_PARENT,
3178 GTK_RESPONSE_ACCEPT,
3180 GTK_RESPONSE_REJECT,
3183 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3184 GtkWidget *response_w = NULL;
3185 #if GTK_CHECK_VERSION (2, 20, 0)
3186 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3190 gtk_widget_grab_focus ( response_w );
3192 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3194 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3195 datasource_gps_clean_up ( dgs );
3196 gtk_widget_destroy ( dialog );
3200 // Get info from reused datasource dialog widgets
3201 gchar* protocol = datasource_gps_get_protocol ( dgs );
3202 gchar* port = datasource_gps_get_descriptor ( dgs );
3203 // NB don't free the above strings as they're references to values held elsewhere
3204 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3205 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3206 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3207 gboolean turn_off = datasource_gps_get_off ( dgs );
3209 gtk_widget_destroy ( dialog );
3211 // When called from the viewport - work the corresponding layerspanel:
3213 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3216 // Apply settings to transfer to the GPS device
3223 vik_layers_panel_get_viewport (vlp),
3232 * Acquire into this TRW Layer from any GPS Babel supported file
3234 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3236 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3237 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3238 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3239 VikViewport *vvp = vik_window_viewport(vw);
3241 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3244 static void trw_layer_new_wp ( gpointer lav[2] )
3246 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3247 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3248 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3249 instead return true if you want to update. */
3250 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 ) {
3251 trw_layer_calculate_bounds_waypoints ( vtl );
3252 vik_layers_panel_emit_update ( vlp );
3256 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3258 vtl->current_track = vik_track_new();
3259 vtl->current_track->visible = TRUE;
3260 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3261 // Create track with the preferred colour from the layer properties
3262 vtl->current_track->color = vtl->track_color;
3264 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3265 vtl->current_track->has_color = TRUE;
3266 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3269 static void trw_layer_new_track ( gpointer lav[2] )
3271 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3273 if ( ! vtl->current_track ) {
3274 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3275 new_track_create_common ( vtl, name );
3277 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3281 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3283 vtl->current_track = vik_track_new();
3284 vtl->current_track->visible = TRUE;
3285 vtl->current_track->is_route = TRUE;
3286 // By default make all routes red
3287 vtl->current_track->has_color = TRUE;
3288 gdk_color_parse ( "red", &vtl->current_track->color );
3289 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3292 static void trw_layer_new_route ( gpointer lav[2] )
3294 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3296 if ( ! vtl->current_track ) {
3297 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3298 new_route_create_common ( vtl, name );
3299 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3303 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3305 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3306 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3308 if ( g_hash_table_size (vtl->routes) > 0 ) {
3309 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3310 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3311 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3312 vik_layers_panel_emit_update ( vlp );
3317 static void trw_layer_finish_track ( gpointer lav[2] )
3319 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3320 vtl->current_track = NULL;
3321 vik_layer_emit_update ( VIK_LAYER(vtl) );
3324 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3326 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3327 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3329 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3330 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3331 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3332 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3333 vik_layers_panel_emit_update ( vlp );
3337 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3339 /* NB do not care if wp is visible or not */
3340 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3343 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3345 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3346 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3348 /* Only 1 waypoint - jump straight to it */
3349 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3350 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3351 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3353 /* If at least 2 waypoints - find center and then zoom to fit */
3354 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3356 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3357 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3358 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3361 vik_layers_panel_emit_update ( vlp );
3364 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3366 static gpointer pass_along[2];
3368 GtkWidget *export_submenu;
3369 pass_along[0] = vtl;
3370 pass_along[1] = vlp;
3372 item = gtk_menu_item_new();
3373 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3374 gtk_widget_show ( item );
3376 if ( vtl->current_track ) {
3377 if ( vtl->current_track->is_route )
3378 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3380 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3381 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3382 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3383 gtk_widget_show ( item );
3386 item = gtk_menu_item_new ();
3387 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3388 gtk_widget_show ( item );
3391 /* Now with icons */
3392 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3393 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3395 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3396 gtk_widget_show ( item );
3398 GtkWidget *view_submenu = gtk_menu_new();
3399 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3400 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3401 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3402 gtk_widget_show ( item );
3403 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3405 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3406 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3407 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3408 gtk_widget_show ( item );
3410 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3412 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3413 gtk_widget_show ( item );
3415 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3416 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3417 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3418 gtk_widget_show ( item );
3420 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3421 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3423 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3424 gtk_widget_show ( item );
3426 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3427 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3428 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3429 gtk_widget_show ( item );
3431 export_submenu = gtk_menu_new ();
3432 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3433 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3434 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3435 gtk_widget_show ( item );
3436 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3438 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3439 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3440 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3441 gtk_widget_show ( item );
3443 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3444 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3445 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3446 gtk_widget_show ( item );
3448 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3449 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3450 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3451 gtk_widget_show ( item );
3453 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3455 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3456 gtk_widget_show ( item );
3458 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3459 item = gtk_menu_item_new_with_mnemonic ( external1 );
3460 g_free ( external1 );
3461 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3462 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3463 gtk_widget_show ( item );
3465 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3466 item = gtk_menu_item_new_with_mnemonic ( external2 );
3467 g_free ( external2 );
3468 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3469 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3470 gtk_widget_show ( item );
3472 GtkWidget *new_submenu = gtk_menu_new();
3473 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3474 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3475 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3476 gtk_widget_show(item);
3477 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3479 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3480 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3481 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3482 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3483 gtk_widget_show ( item );
3485 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3486 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3487 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3488 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3489 gtk_widget_show ( item );
3490 // Make it available only when a new track *not* already in progress
3491 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3493 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3494 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3495 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3496 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3497 gtk_widget_show ( item );
3498 // Make it available only when a new track *not* already in progress
3499 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3501 #ifdef VIK_CONFIG_GEOTAG
3502 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3503 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3504 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3505 gtk_widget_show ( item );
3508 GtkWidget *acquire_submenu = gtk_menu_new ();
3509 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3510 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3511 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3512 gtk_widget_show ( item );
3513 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3515 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3516 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3517 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3518 gtk_widget_show ( item );
3520 #ifdef VIK_CONFIG_GOOGLE
3521 item = gtk_menu_item_new_with_mnemonic ( _("From Google _Directions...") );
3522 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3523 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3524 gtk_widget_show ( item );
3527 #ifdef VIK_CONFIG_OPENSTREETMAP
3528 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3530 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3531 gtk_widget_show ( item );
3533 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3535 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3536 gtk_widget_show ( item );
3539 #ifdef VIK_CONFIG_GEONAMES
3540 GtkWidget *wikipedia_submenu = gtk_menu_new();
3541 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3542 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3543 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3544 gtk_widget_show(item);
3545 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3547 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3548 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3550 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3551 gtk_widget_show ( item );
3553 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3554 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3555 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3556 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3557 gtk_widget_show ( item );
3560 #ifdef VIK_CONFIG_GEOCACHES
3561 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3562 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3563 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3564 gtk_widget_show ( item );
3567 #ifdef VIK_CONFIG_GEOTAG
3568 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3569 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3570 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3571 gtk_widget_show ( item );
3574 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3575 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3576 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3577 gtk_widget_show ( item );
3579 GtkWidget *upload_submenu = gtk_menu_new ();
3580 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3581 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3582 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3583 gtk_widget_show ( item );
3584 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3586 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3587 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3588 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3589 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3590 gtk_widget_show ( item );
3592 #ifdef VIK_CONFIG_OPENSTREETMAP
3593 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3594 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3595 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3596 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3597 gtk_widget_show ( item );
3600 GtkWidget *delete_submenu = gtk_menu_new ();
3601 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3602 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3603 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3604 gtk_widget_show ( item );
3605 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3607 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
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_tracks), 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 Tracks _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_tracks_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 Routes") );
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_routes), 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 Routes 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_routes_from_selection), pass_along );
3628 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3629 gtk_widget_show ( item );
3631 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3632 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3633 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3634 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3635 gtk_widget_show ( item );
3637 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3638 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3639 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3640 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3641 gtk_widget_show ( item );
3643 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3644 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3646 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3647 gtk_widget_show ( item );
3650 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3651 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3653 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3654 gtk_widget_show ( item );
3658 // Fake Waypoint UUIDs vi simple increasing integer
3659 static guint wp_uuid = 0;
3661 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3665 vik_waypoint_set_name (wp, name);
3667 if ( VIK_LAYER(vtl)->realized )
3669 // Do we need to create the sublayer:
3670 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3671 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3674 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3676 // Visibility column always needed for waypoints
3677 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3678 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 );
3680 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 );
3682 // Actual setting of visibility dependent on the waypoint
3683 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3685 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3688 highest_wp_number_add_wp(vtl, name);
3689 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3693 // Fake Track UUIDs vi simple increasing integer
3694 static guint tr_uuid = 0;
3696 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3700 vik_track_set_name (t, name);
3702 if ( VIK_LAYER(vtl)->realized )
3704 // Do we need to create the sublayer:
3705 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3706 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3709 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3710 // Visibility column always needed for tracks
3711 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3712 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 );
3714 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 );
3716 // Actual setting of visibility dependent on the track
3717 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3719 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3722 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3724 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3727 // Fake Route UUIDs vi simple increasing integer
3728 static guint rt_uuid = 0;
3730 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3734 vik_track_set_name (t, name);
3736 if ( VIK_LAYER(vtl)->realized )
3738 // Do we need to create the sublayer:
3739 if ( g_hash_table_size (vtl->routes) == 0 ) {
3740 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3743 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3744 // Visibility column always needed for tracks
3745 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3746 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 );
3748 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 );
3750 // Actual setting of visibility dependent on the track
3751 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3753 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3756 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3758 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3761 /* to be called whenever a track has been deleted or may have been changed. */
3762 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3764 if (vtl->current_tp_track == trk )
3765 trw_layer_cancel_current_tp ( vtl, FALSE );
3769 * Normally this is done to due the waypoint size preference having changed
3771 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
3773 GHashTableIter iter;
3774 gpointer key, value;
3777 g_hash_table_iter_init ( &iter, vtl->waypoints );
3778 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
3779 VikWaypoint *wp = VIK_WAYPOINT(value);
3781 // Reapply symbol setting to update the pixbuf
3782 gchar *tmp_symbol = g_strdup ( wp->symbol );
3783 vik_waypoint_set_symbol ( wp, tmp_symbol );
3784 g_free ( tmp_symbol );
3789 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3792 gchar *newname = g_strdup(name);
3797 switch ( sublayer_type ) {
3798 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3799 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3801 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3802 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3805 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3808 // If found a name already in use try adding 1 to it and we try again
3810 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3812 newname = new_newname;
3815 } while ( id != NULL);
3820 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3822 // No more uniqueness of name forced when loading from a file
3823 // This now makes this function a little redunant as we just flow the parameters through
3824 vik_trw_layer_add_waypoint ( vtl, name, wp );
3827 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3829 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3830 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3831 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3832 vik_track_free ( tr );
3833 vtl->route_finder_append = FALSE; /* this means we have added it */
3836 // No more uniqueness of name forced when loading from a file
3838 vik_trw_layer_add_route ( vtl, name, tr );
3840 vik_trw_layer_add_track ( vtl, name, tr );
3842 if ( vtl->route_finder_check_added_track ) {
3843 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3844 vtl->route_finder_added_track = tr;
3849 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3851 *l = g_list_append(*l, id);
3855 * Move an item from one TRW layer to another TRW layer
3857 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3859 gboolean rename = ( vtl_src != vtl_dest );
3861 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3862 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3866 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3868 newname = g_strdup ( trk->name );
3870 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3871 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3872 vik_trw_layer_delete_track ( vtl_src, trk );
3875 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3876 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3880 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3882 newname = g_strdup ( trk->name );
3884 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3885 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3886 vik_trw_layer_delete_route ( vtl_src, trk );
3889 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3890 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3894 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
3896 newname = g_strdup ( wp->name );
3898 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3899 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3900 trw_layer_delete_waypoint ( vtl_src, wp );
3902 // If no change - don't need to recalculate bounds
3904 trw_layer_calculate_bounds_waypoints ( vtl_dest );
3905 trw_layer_calculate_bounds_waypoints ( vtl_src );
3910 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3912 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3913 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3915 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3916 GList *items = NULL;
3919 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3920 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3922 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3923 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3925 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3926 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3931 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3932 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3934 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3935 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3937 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3944 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3945 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3950 VikTrack *trk; // input
3951 gpointer uuid; // output
3954 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3956 trku_udata *user_data = udata;
3957 if ( trk == user_data->trk ) {
3958 user_data->uuid = id;
3964 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3966 gboolean was_visible = FALSE;
3968 if ( trk && trk->name ) {
3970 if ( trk == vtl->current_track ) {
3971 vtl->current_track = NULL;
3972 vtl->current_tp_track = NULL;
3973 vtl->current_tp_id = NULL;
3974 vtl->moving_tp = FALSE;
3977 was_visible = trk->visible;
3979 if ( trk == vtl->route_finder_current_track )
3980 vtl->route_finder_current_track = NULL;
3982 if ( trk == vtl->route_finder_added_track )
3983 vtl->route_finder_added_track = NULL;
3989 // Hmmm, want key of it
3990 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3992 if ( trkf && udata.uuid ) {
3993 /* could be current_tp, so we have to check */
3994 trw_layer_cancel_tps_of_track ( vtl, trk );
3996 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3999 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4000 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4001 g_hash_table_remove ( vtl->tracks, udata.uuid );
4003 // If last sublayer, then remove sublayer container
4004 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4005 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4013 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4015 gboolean was_visible = FALSE;
4017 if ( trk && trk->name ) {
4019 if ( trk == vtl->current_track ) {
4020 vtl->current_track = NULL;
4021 vtl->current_tp_track = NULL;
4022 vtl->current_tp_id = NULL;
4023 vtl->moving_tp = FALSE;
4026 was_visible = trk->visible;
4028 if ( trk == vtl->route_finder_current_track )
4029 vtl->route_finder_current_track = NULL;
4031 if ( trk == vtl->route_finder_added_track )
4032 vtl->route_finder_added_track = NULL;
4038 // Hmmm, want key of it
4039 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4041 if ( trkf && udata.uuid ) {
4042 /* could be current_tp, so we have to check */
4043 trw_layer_cancel_tps_of_track ( vtl, trk );
4045 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4048 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4049 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4050 g_hash_table_remove ( vtl->routes, udata.uuid );
4052 // If last sublayer, then remove sublayer container
4053 if ( g_hash_table_size (vtl->routes) == 0 ) {
4054 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4062 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4064 gboolean was_visible = FALSE;
4066 if ( wp && wp->name ) {
4068 if ( wp == vtl->current_wp ) {
4069 vtl->current_wp = NULL;
4070 vtl->current_wp_id = NULL;
4071 vtl->moving_wp = FALSE;
4074 was_visible = wp->visible;
4080 // Hmmm, want key of it
4081 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4083 if ( wpf && udata.uuid ) {
4084 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4087 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4088 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4090 highest_wp_number_remove_wp(vtl, wp->name);
4091 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4093 // If last sublayer, then remove sublayer container
4094 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4095 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4105 // Only for temporary use by trw_layer_delete_waypoint_by_name
4106 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4108 wpu_udata *user_data = udata;
4109 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4110 user_data->uuid = id;
4117 * Delete a waypoint by the given name
4118 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4119 * as there be multiple waypoints with the same name
4121 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4124 // Fake a waypoint with the given name
4125 udata.wp = vik_waypoint_new ();
4126 vik_waypoint_set_name (udata.wp, name);
4127 // Currently only the name is used in this waypoint find function
4130 // Hmmm, want key of it
4131 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4133 vik_waypoint_free (udata.wp);
4135 if ( wpf && udata.uuid )
4136 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4142 VikTrack *trk; // input
4143 gpointer uuid; // output
4146 // Only for temporary use by trw_layer_delete_track_by_name
4147 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4149 tpu_udata *user_data = udata;
4150 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4151 user_data->uuid = id;
4158 * Delete a track by the given name
4159 * NOTE: ATM this will delete the first encountered Track with the specified name
4160 * as there may be multiple tracks with the same name within the specified hash table
4162 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4165 // Fake a track with the given name
4166 udata.trk = vik_track_new ();
4167 vik_track_set_name (udata.trk, name);
4168 // Currently only the name is used in this waypoint find function
4171 // Hmmm, want key of it
4172 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4174 vik_track_free (udata.trk);
4176 if ( trkf && udata.uuid ) {
4177 // This could be a little better written...
4178 if ( vtl->tracks == ht_tracks )
4179 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4180 if ( vtl->routes == ht_tracks )
4181 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4188 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4190 vik_treeview_item_delete (vt, it );
4193 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4196 vtl->current_track = NULL;
4197 vtl->route_finder_current_track = NULL;
4198 vtl->route_finder_added_track = NULL;
4199 if (vtl->current_tp_track)
4200 trw_layer_cancel_current_tp(vtl, FALSE);
4202 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4203 g_hash_table_remove_all(vtl->routes_iters);
4204 g_hash_table_remove_all(vtl->routes);
4206 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4208 vik_layer_emit_update ( VIK_LAYER(vtl) );
4211 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4214 vtl->current_track = NULL;
4215 vtl->route_finder_current_track = NULL;
4216 vtl->route_finder_added_track = NULL;
4217 if (vtl->current_tp_track)
4218 trw_layer_cancel_current_tp(vtl, FALSE);
4220 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4221 g_hash_table_remove_all(vtl->tracks_iters);
4222 g_hash_table_remove_all(vtl->tracks);
4224 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4226 vik_layer_emit_update ( VIK_LAYER(vtl) );
4229 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4231 vtl->current_wp = NULL;
4232 vtl->current_wp_id = NULL;
4233 vtl->moving_wp = FALSE;
4235 highest_wp_number_reset(vtl);
4237 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4238 g_hash_table_remove_all(vtl->waypoints_iters);
4239 g_hash_table_remove_all(vtl->waypoints);
4241 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4243 vik_layer_emit_update ( VIK_LAYER(vtl) );
4246 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4248 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4249 // Get confirmation from the user
4250 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4251 _("Are you sure you want to delete all tracks in %s?"),
4252 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4253 vik_trw_layer_delete_all_tracks (vtl);
4256 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4258 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4259 // Get confirmation from the user
4260 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4261 _("Are you sure you want to delete all routes in %s?"),
4262 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4263 vik_trw_layer_delete_all_routes (vtl);
4266 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4268 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4269 // Get confirmation from the user
4270 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4271 _("Are you sure you want to delete all waypoints in %s?"),
4272 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4273 vik_trw_layer_delete_all_waypoints (vtl);
4276 static void trw_layer_delete_item ( gpointer pass_along[6] )
4278 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4279 gboolean was_visible = FALSE;
4280 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4282 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4283 if ( wp && wp->name ) {
4284 if ( GPOINTER_TO_INT ( pass_along[4]) )
4285 // Get confirmation from the user
4286 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4287 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4288 _("Are you sure you want to delete the waypoint \"%s\""),
4291 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4294 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4296 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4297 if ( trk && trk->name ) {
4298 if ( GPOINTER_TO_INT ( pass_along[4]) )
4299 // Get confirmation from the user
4300 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4301 _("Are you sure you want to delete the track \"%s\""),
4304 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4309 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4310 if ( trk && trk->name ) {
4311 if ( GPOINTER_TO_INT ( pass_along[4]) )
4312 // Get confirmation from the user
4313 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4314 _("Are you sure you want to delete the route \"%s\""),
4317 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4321 vik_layer_emit_update ( VIK_LAYER(vtl) );
4325 static void trw_layer_properties_item ( gpointer pass_along[7] )
4327 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4328 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4330 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4332 if ( wp && wp->name )
4334 gboolean updated = FALSE;
4335 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4337 if ( updated && pass_along[6] )
4338 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4340 if ( updated && VIK_LAYER(vtl)->visible )
4341 vik_layer_emit_update ( VIK_LAYER(vtl) );
4347 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4348 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4350 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4352 if ( tr && tr->name )
4354 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4357 pass_along[1], /* vlp */
4358 pass_along[5], /* vvp */
4359 pass_along[6]); /* iter */
4365 * Update the treeview of the track id - primarily to update the icon
4367 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4373 gpointer *trkf = NULL;
4374 if ( trk->is_route )
4375 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4377 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4379 if ( trkf && udata.uuid ) {
4381 GtkTreeIter *iter = NULL;
4382 if ( trk->is_route )
4383 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4385 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4388 // TODO: Make this a function
4389 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4390 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4391 ((trk->color.green & 0xff00) << 8) |
4392 (trk->color.blue & 0xff00);
4393 gdk_pixbuf_fill ( pixbuf, pixel );
4394 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4395 g_object_unref (pixbuf);
4402 Parameter 1 -> VikLayersPanel
4403 Parameter 2 -> VikLayer
4404 Parameter 3 -> VikViewport
4406 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4409 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4410 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4413 /* since vlp not set, vl & vvp should be valid instead! */
4415 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4416 vik_layer_emit_update ( VIK_LAYER(vl) );
4421 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4423 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4425 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4426 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4428 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4430 if ( track && track->trackpoints )
4431 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4434 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4436 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4438 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4439 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4441 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4443 if ( track && track->trackpoints )
4445 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4447 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4448 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4449 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4450 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4451 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4455 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4457 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4459 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4460 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4462 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4467 // Converting a track to a route can be a bit more complicated,
4468 // so give a chance to change our minds:
4469 if ( !trk->is_route &&
4470 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4471 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4473 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4474 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4479 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4482 trk_copy->is_route = !trk_copy->is_route;
4484 // ATM can't set name to self - so must create temporary copy
4485 gchar *name = g_strdup ( trk_copy->name );
4487 // Delete old one and then add new one
4488 if ( trk->is_route ) {
4489 vik_trw_layer_delete_route ( vtl, trk );
4490 vik_trw_layer_add_track ( vtl, name, trk_copy );
4493 // Extra route conversion bits...
4494 vik_track_merge_segments ( trk_copy );
4495 vik_track_to_routepoints ( trk_copy );
4497 vik_trw_layer_delete_track ( vtl, trk );
4498 vik_trw_layer_add_route ( vtl, name, trk_copy );
4502 // Update in case color of track / route changes when moving between sublayers
4503 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4507 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4509 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4511 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4512 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4514 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4519 vtl->current_track = track;
4520 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);
4522 if ( track->trackpoints )
4523 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4526 #ifdef VIK_CONFIG_GOOGLE
4528 * extend a track using route finder
4530 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4532 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4533 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4536 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4538 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4539 vtl->route_finder_coord = last_coord;
4540 vtl->route_finder_current_track = track;
4541 vtl->route_finder_started = TRUE;
4543 if ( track->trackpoints )
4544 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4549 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4551 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4552 /* Also warn if overwrite old elevation data */
4553 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4555 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4556 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4558 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4561 vik_track_apply_dem_data ( track );
4564 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4566 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4568 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4569 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4571 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4576 GList *trps = track->trackpoints;
4579 trps = g_list_last(trps);
4580 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4583 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4585 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4587 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4588 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4590 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4595 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4598 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4601 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4603 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4605 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4606 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4608 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4613 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4616 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4619 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4621 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4623 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4624 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4626 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4631 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4634 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4638 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4640 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4642 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4644 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4645 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4647 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4649 if ( trk && trk->trackpoints )
4651 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4652 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4653 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4654 if ( pass_along[1] )
4655 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4657 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4661 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4663 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4664 trw_layer_tpwin_init ( vtl );
4667 /*************************************
4668 * merge/split by time routines
4669 *************************************/
4671 /* called for each key in track hash table.
4672 * If the current track has the same time stamp type, add it to the result,
4673 * except the one pointed by "exclude".
4674 * set exclude to NULL if there is no exclude to check.
4675 * Note that the result is in reverse (for performance reasons).
4680 gboolean with_timestamps;
4682 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4684 twt_udata *user_data = udata;
4685 VikTrackpoint *p1, *p2;
4687 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4691 if (VIK_TRACK(value)->trackpoints) {
4692 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4693 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4695 if ( user_data->with_timestamps ) {
4696 if (!p1->has_timestamp || !p2->has_timestamp) {
4701 // Don't add tracks with timestamps when getting non timestamp tracks
4702 if (p1->has_timestamp || p2->has_timestamp) {
4708 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4711 /* called for each key in track hash table. if original track user_data[1] is close enough
4712 * to the passed one, add it to list in user_data[0]
4714 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4717 VikTrackpoint *p1, *p2;
4718 VikTrack *trk = VIK_TRACK(value);
4720 GList **nearby_tracks = ((gpointer *)user_data)[0];
4721 GList *tpoints = ((gpointer *)user_data)[1];
4724 * detect reasons for not merging, and return
4725 * if no reason is found not to merge, then do it.
4728 // Exclude the original track from the compiled list
4729 if (trk->trackpoints == tpoints) {
4733 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4734 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4736 if (trk->trackpoints) {
4737 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4738 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4740 if (!p1->has_timestamp || !p2->has_timestamp) {
4741 //g_print("no timestamp\n");
4745 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4746 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4747 if (! (abs(t1 - p2->timestamp) < threshold ||
4749 abs(p1->timestamp - t2) < threshold)
4756 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4759 /* comparison function used to sort tracks; a and b are hash table keys */
4760 /* Not actively used - can be restored if needed
4761 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4763 GHashTable *tracks = user_data;
4766 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4767 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4769 if (t1 < t2) return -1;
4770 if (t1 > t2) return 1;
4775 /* comparison function used to sort trackpoints */
4776 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4778 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4780 if (t1 < t2) return -1;
4781 if (t1 > t2) return 1;
4786 * comparison function which can be used to sort tracks or waypoints by name
4788 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4790 const gchar* namea = (const gchar*) a;
4791 const gchar* nameb = (const gchar*) b;
4792 if ( namea == NULL || nameb == NULL)
4795 // Same sort method as used in the vik_treeview_*_alphabetize functions
4796 return strcmp ( namea, nameb );
4800 * Attempt to merge selected track with other tracks specified by the user
4801 * Tracks to merge with must be of the same 'type' as the selected track -
4802 * either all with timestamps, or all without timestamps
4804 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4806 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4807 GList *other_tracks = NULL;
4808 GHashTable *ght_tracks;
4809 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4810 ght_tracks = vtl->routes;
4812 ght_tracks = vtl->tracks;
4814 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4819 if ( !track->trackpoints )
4823 udata.result = &other_tracks;
4824 udata.exclude = track->trackpoints;
4825 // Allow merging with 'similar' time type time tracks
4826 // i.e. either those times, or those without
4827 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4829 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4830 other_tracks = g_list_reverse(other_tracks);
4832 if ( !other_tracks ) {
4833 if ( udata.with_timestamps )
4834 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4836 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4840 // Sort alphabetically for user presentation
4841 // Convert into list of names for usage with dialog function
4842 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4843 GList *other_tracks_names = NULL;
4844 GList *iter = g_list_first ( other_tracks );
4846 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4847 iter = g_list_next ( iter );
4850 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4852 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4856 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4857 g_list_free(other_tracks);
4858 g_list_free(other_tracks_names);
4863 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4864 VikTrack *merge_track;
4865 if ( track->is_route )
4866 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4868 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4871 vik_track_steal_and_append_trackpoints ( track, merge_track );
4872 if ( track->is_route )
4873 vik_trw_layer_delete_route (vtl, merge_track);
4875 vik_trw_layer_delete_track (vtl, merge_track);
4876 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4879 for (l = merge_list; l != NULL; l = g_list_next(l))
4881 g_list_free(merge_list);
4883 vik_layer_emit_update( VIK_LAYER(vtl) );
4887 // c.f. trw_layer_sorted_track_id_by_name_list
4888 // but don't add the specified track to the list (normally current track)
4889 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4891 twt_udata *user_data = udata;
4894 if (trk->trackpoints == user_data->exclude) {
4898 // Sort named list alphabetically
4899 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4903 * Join - this allows combining 'tracks' and 'track routes'
4904 * i.e. doesn't care about whether tracks have consistent timestamps
4905 * ATM can only append one track at a time to the currently selected track
4907 static void trw_layer_append_track ( gpointer pass_along[6] )
4910 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4912 GHashTable *ght_tracks;
4913 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4914 ght_tracks = vtl->routes;
4916 ght_tracks = vtl->tracks;
4918 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4923 GList *other_tracks_names = NULL;
4925 // Sort alphabetically for user presentation
4926 // Convert into list of names for usage with dialog function
4927 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4929 udata.result = &other_tracks_names;
4930 udata.exclude = trk->trackpoints;
4932 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4934 // Note the limit to selecting one track only
4935 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4936 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4937 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4940 trk->is_route ? _("Append Route"): _("Append Track"),
4941 trk->is_route ? _("Select the route to append after the current route") :
4942 _("Select the track to append after the current track") );
4944 g_list_free(other_tracks_names);
4946 // It's a list, but shouldn't contain more than one other track!
4947 if ( append_list ) {
4949 for (l = append_list; l != NULL; l = g_list_next(l)) {
4950 // TODO: at present this uses the first track found by name,
4951 // which with potential multiple same named tracks may not be the one selected...
4952 VikTrack *append_track;
4953 if ( trk->is_route )
4954 append_track = vik_trw_layer_get_route ( vtl, l->data );
4956 append_track = vik_trw_layer_get_track ( vtl, l->data );
4958 if ( append_track ) {
4959 vik_track_steal_and_append_trackpoints ( trk, append_track );
4960 if ( trk->is_route )
4961 vik_trw_layer_delete_route (vtl, append_track);
4963 vik_trw_layer_delete_track (vtl, append_track);
4966 for (l = append_list; l != NULL; l = g_list_next(l))
4968 g_list_free(append_list);
4970 vik_layer_emit_update( VIK_LAYER(vtl) );
4975 * Very similar to trw_layer_append_track for joining
4976 * but this allows selection from the 'other' list
4977 * If a track is selected, then is shows routes and joins the selected one
4978 * If a route is selected, then is shows tracks and joins the selected one
4980 static void trw_layer_append_other ( gpointer pass_along[6] )
4983 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4985 GHashTable *ght_mykind, *ght_others;
4986 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4987 ght_mykind = vtl->routes;
4988 ght_others = vtl->tracks;
4991 ght_mykind = vtl->tracks;
4992 ght_others = vtl->routes;
4995 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5000 GList *other_tracks_names = NULL;
5002 // Sort alphabetically for user presentation
5003 // Convert into list of names for usage with dialog function
5004 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5006 udata.result = &other_tracks_names;
5007 udata.exclude = trk->trackpoints;
5009 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5011 // Note the limit to selecting one track only
5012 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5013 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5014 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5017 trk->is_route ? _("Append Track"): _("Append Route"),
5018 trk->is_route ? _("Select the track to append after the current route") :
5019 _("Select the route to append after the current track") );
5021 g_list_free(other_tracks_names);
5023 // It's a list, but shouldn't contain more than one other track!
5024 if ( append_list ) {
5026 for (l = append_list; l != NULL; l = g_list_next(l)) {
5027 // TODO: at present this uses the first track found by name,
5028 // which with potential multiple same named tracks may not be the one selected...
5030 // Get FROM THE OTHER TYPE list
5031 VikTrack *append_track;
5032 if ( trk->is_route )
5033 append_track = vik_trw_layer_get_track ( vtl, l->data );
5035 append_track = vik_trw_layer_get_route ( vtl, l->data );
5037 if ( append_track ) {
5039 if ( !append_track->is_route &&
5040 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5041 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5043 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5044 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5045 vik_track_merge_segments ( append_track );
5046 vik_track_to_routepoints ( append_track );
5053 vik_track_steal_and_append_trackpoints ( trk, append_track );
5055 // Delete copied which is FROM THE OTHER TYPE list
5056 if ( trk->is_route )
5057 vik_trw_layer_delete_track (vtl, append_track);
5059 vik_trw_layer_delete_route (vtl, append_track);
5062 for (l = append_list; l != NULL; l = g_list_next(l))
5064 g_list_free(append_list);
5065 vik_layer_emit_update( VIK_LAYER(vtl) );
5069 /* merge by segments */
5070 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5072 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5073 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5074 guint segments = vik_track_merge_segments ( trk );
5075 // NB currently no need to redraw as segments not actually shown on the display
5076 // However inform the user of what happened:
5078 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5079 g_snprintf(str, 64, tmp_str, segments);
5080 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5083 /* merge by time routine */
5084 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5086 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5090 GList *tracks_with_timestamp = NULL;
5091 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5092 if (orig_trk->trackpoints &&
5093 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5094 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5099 udata.result = &tracks_with_timestamp;
5100 udata.exclude = orig_trk->trackpoints;
5101 udata.with_timestamps = TRUE;
5102 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5103 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5105 if (!tracks_with_timestamp) {
5106 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5109 g_list_free(tracks_with_timestamp);
5111 static guint threshold_in_minutes = 1;
5112 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5113 _("Merge Threshold..."),
5114 _("Merge when time between tracks less than:"),
5115 &threshold_in_minutes)) {
5119 // keep attempting to merge all tracks until no merges within the time specified is possible
5120 gboolean attempt_merge = TRUE;
5121 GList *nearby_tracks = NULL;
5123 static gpointer params[3];
5125 while ( attempt_merge ) {
5127 // Don't try again unless tracks have changed
5128 attempt_merge = FALSE;
5130 trps = orig_trk->trackpoints;
5134 if (nearby_tracks) {
5135 g_list_free(nearby_tracks);
5136 nearby_tracks = NULL;
5139 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5140 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5142 /* g_print("Original track times: %d and %d\n", t1, t2); */
5143 params[0] = &nearby_tracks;
5144 params[1] = (gpointer)trps;
5145 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5147 /* get a list of adjacent-in-time tracks */
5148 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5151 GList *l = nearby_tracks;
5154 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5155 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5157 t1 = get_first_trackpoint(l)->timestamp;
5158 t2 = get_last_trackpoint(l)->timestamp;
5159 #undef get_first_trackpoint
5160 #undef get_last_trackpoint
5161 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5164 /* remove trackpoints from merged track, delete track */
5165 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5166 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5168 // Tracks have changed, therefore retry again against all the remaining tracks
5169 attempt_merge = TRUE;
5174 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5177 g_list_free(nearby_tracks);
5179 vik_layer_emit_update( VIK_LAYER(vtl) );
5183 * Split a track at the currently selected trackpoint
5185 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5187 if ( !vtl->current_tpl )
5190 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5191 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5193 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5194 GList *newglist = g_list_alloc ();
5195 newglist->prev = NULL;
5196 newglist->next = vtl->current_tpl->next;
5197 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5198 tr->trackpoints = newglist;
5200 vtl->current_tpl->next->prev = newglist; /* end old track here */
5201 vtl->current_tpl->next = NULL;
5203 // Bounds of the selected track changed due to the split
5204 vik_track_calculate_bounds ( vtl->current_tp_track );
5206 vtl->current_tpl = newglist; /* change tp to first of new track. */
5207 vtl->current_tp_track = tr;
5210 vik_trw_layer_add_route ( vtl, name, tr );
5212 vik_trw_layer_add_track ( vtl, name, tr );
5214 // Bounds of the new track created by the split
5215 vik_track_calculate_bounds ( tr );
5221 // Also need id of newly created track
5224 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5226 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5228 if ( trkf && udata.uuid )
5229 vtl->current_tp_id = udata.uuid;
5231 vtl->current_tp_id = NULL;
5233 vik_layer_emit_update(VIK_LAYER(vtl));
5238 /* split by time routine */
5239 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5241 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5242 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5243 GList *trps = track->trackpoints;
5245 GList *newlists = NULL;
5246 GList *newtps = NULL;
5247 static guint thr = 1;
5254 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5255 _("Split Threshold..."),
5256 _("Split when time between trackpoints exceeds:"),
5261 /* iterate through trackpoints, and copy them into new lists without touching original list */
5262 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5266 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5268 g_print("panic: ts < prev_ts: this should never happen!\n");
5271 if (ts - prev_ts > thr*60) {
5272 /* flush accumulated trackpoints into new list */
5273 newlists = g_list_append(newlists, g_list_reverse(newtps));
5277 /* accumulate trackpoint copies in newtps, in reverse order */
5278 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5280 iter = g_list_next(iter);
5283 newlists = g_list_append(newlists, g_list_reverse(newtps));
5286 /* put lists of trackpoints into tracks */
5288 // Only bother updating if the split results in new tracks
5289 if (g_list_length (newlists) > 1) {
5294 tr = vik_track_copy ( track, FALSE );
5295 tr->trackpoints = (GList *)(iter->data);
5297 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5298 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5299 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5300 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5301 vik_track_calculate_bounds ( tr );
5303 iter = g_list_next(iter);
5305 // Remove original track and then update the display
5306 vik_trw_layer_delete_track (vtl, track);
5307 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5309 g_list_free(newlists);
5313 * Split a track by the number of points as specified by the user
5315 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5317 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5319 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5320 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5322 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5327 // Check valid track
5328 GList *trps = track->trackpoints;
5332 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5333 _("Split Every Nth Point"),
5334 _("Split on every Nth point:"),
5335 250, // Default value as per typical limited track capacity of various GPS devices
5339 // Was a valid number returned?
5345 GList *newlists = NULL;
5346 GList *newtps = NULL;
5351 /* accumulate trackpoint copies in newtps, in reverse order */
5352 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5354 if (count >= points) {
5355 /* flush accumulated trackpoints into new list */
5356 newlists = g_list_append(newlists, g_list_reverse(newtps));
5360 iter = g_list_next(iter);
5363 // If there is a remaining chunk put that into the new split list
5364 // This may well be the whole track if no split points were encountered
5366 newlists = g_list_append(newlists, g_list_reverse(newtps));
5369 /* put lists of trackpoints into tracks */
5371 // Only bother updating if the split results in new tracks
5372 if (g_list_length (newlists) > 1) {
5377 tr = vik_track_copy ( track, FALSE );
5378 tr->trackpoints = (GList *)(iter->data);
5380 if ( track->is_route ) {
5381 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5382 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5385 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5386 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5388 vik_track_calculate_bounds ( tr );
5390 iter = g_list_next(iter);
5392 // Remove original track and then update the display
5393 if ( track->is_route )
5394 vik_trw_layer_delete_route (vtl, track);
5396 vik_trw_layer_delete_track (vtl, track);
5397 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5399 g_list_free(newlists);
5403 * Split a track at the currently selected trackpoint
5405 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5407 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5408 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5409 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5413 * Split a track by its segments
5414 * Routes do not have segments so don't call this for routes
5416 static void trw_layer_split_segments ( gpointer pass_along[6] )
5418 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5419 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5426 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5429 for ( i = 0; i < ntracks; i++ ) {
5431 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5432 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5437 // Remove original track
5438 vik_trw_layer_delete_track ( vtl, trk );
5439 vik_layer_emit_update ( VIK_LAYER(vtl) );
5442 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5445 /* end of split/merge routines */
5448 * Delete adjacent track points at the same position
5449 * AKA Delete Dulplicates on the Properties Window
5451 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5453 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5455 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5456 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5458 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5463 gulong removed = vik_track_remove_dup_points ( trk );
5465 // Track has been updated so update tps:
5466 trw_layer_cancel_tps_of_track ( vtl, trk );
5468 // Inform user how much was deleted as it's not obvious from the normal view
5470 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5471 g_snprintf(str, 64, tmp_str, removed);
5472 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5474 vik_layer_emit_update ( VIK_LAYER(vtl) );
5478 * Delete adjacent track points with the same timestamp
5479 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5481 static void trw_layer_delete_points_same_time ( 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 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5488 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5493 gulong removed = vik_track_remove_same_time_points ( trk );
5495 // Track has been updated so update tps:
5496 trw_layer_cancel_tps_of_track ( vtl, trk );
5498 // Inform user how much was deleted as it's not obvious from the normal view
5500 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5501 g_snprintf(str, 64, tmp_str, removed);
5502 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5504 vik_layer_emit_update ( VIK_LAYER(vtl) );
5510 static void trw_layer_reverse ( gpointer pass_along[6] )
5512 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5514 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5515 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5517 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5522 vik_track_reverse ( track );
5524 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5528 * Similar to trw_layer_enum_item, but this uses a sorted method
5531 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5533 GList **list = (GList**)udata;
5534 // *list = g_list_prepend(*all, key); //unsorted method
5535 // Sort named list alphabetically
5536 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5541 * Now Waypoint specific sort
5543 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5545 GList **list = (GList**)udata;
5546 // Sort named list alphabetically
5547 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5551 * Track specific sort
5553 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5555 GList **list = (GList**)udata;
5556 // Sort named list alphabetically
5557 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5562 gboolean has_same_track_name;
5563 const gchar *same_track_name;
5564 } same_track_name_udata;
5566 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5568 const gchar* namea = (const gchar*) aa;
5569 const gchar* nameb = (const gchar*) bb;
5572 gint result = strcmp ( namea, nameb );
5574 if ( result == 0 ) {
5575 // Found two names the same
5576 same_track_name_udata *user_data = udata;
5577 user_data->has_same_track_name = TRUE;
5578 user_data->same_track_name = namea;
5581 // Leave ordering the same
5586 * Find out if any tracks have the same name in this hash table
5588 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5590 // Sort items by name, then compare if any next to each other are the same
5592 GList *track_names = NULL;
5593 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5596 if ( ! track_names )
5599 same_track_name_udata udata;
5600 udata.has_same_track_name = FALSE;
5602 // Use sort routine to traverse list comparing items
5603 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5604 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5605 // Still no tracks...
5609 return udata.has_same_track_name;
5613 * Force unqiue track names for the track table specified
5614 * Note the panel is a required parameter to enable the update of the names displayed
5615 * Specify if on tracks or else on routes
5617 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5619 // . Search list for an instance of repeated name
5620 // . get track of this name
5621 // . create new name
5622 // . rename track & update equiv. treeview iter
5623 // . repeat until all different
5625 same_track_name_udata udata;
5627 GList *track_names = NULL;
5628 udata.has_same_track_name = FALSE;
5629 udata.same_track_name = NULL;
5631 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5634 if ( ! track_names )
5637 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5639 // Still no tracks...
5640 if ( ! dummy_list1 )
5643 while ( udata.has_same_track_name ) {
5645 // Find a track with the same name
5648 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5650 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5654 g_critical("Houston, we've had a problem.");
5655 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5656 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5661 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5662 vik_track_set_name ( trk, newname );
5668 // Need want key of it for treeview update
5669 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5671 if ( trkf && udataU.uuid ) {
5675 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5677 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5680 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5681 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5682 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5687 // Start trying to find same names again...
5689 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5690 udata.has_same_track_name = FALSE;
5691 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5693 // No tracks any more - give up searching
5694 if ( ! dummy_list2 )
5695 udata.has_same_track_name = FALSE;
5699 vik_layers_panel_emit_update ( vlp );
5705 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5707 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5710 // Ensure list of track names offered is unique
5711 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5712 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5713 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5714 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5720 // Sort list alphabetically for better presentation
5721 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5724 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5728 // Get list of items to delete from the user
5729 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5732 _("Delete Selection"),
5733 _("Select tracks to delete"));
5736 // Delete requested tracks
5737 // since specificly requested, IMHO no need for extra confirmation
5738 if ( delete_list ) {
5740 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5741 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5742 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5744 g_list_free(delete_list);
5745 vik_layer_emit_update( VIK_LAYER(vtl) );
5752 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5754 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5757 // Ensure list of track names offered is unique
5758 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5759 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5760 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5761 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5767 // Sort list alphabetically for better presentation
5768 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5771 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5775 // Get list of items to delete from the user
5776 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5779 _("Delete Selection"),
5780 _("Select routes to delete") );
5783 // Delete requested routes
5784 // since specificly requested, IMHO no need for extra confirmation
5785 if ( delete_list ) {
5787 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5788 // This deletes first route it finds of that name (but uniqueness is enforced above)
5789 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5791 g_list_free(delete_list);
5792 vik_layer_emit_update( VIK_LAYER(vtl) );
5797 gboolean has_same_waypoint_name;
5798 const gchar *same_waypoint_name;
5799 } same_waypoint_name_udata;
5801 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5803 const gchar* namea = (const gchar*) aa;
5804 const gchar* nameb = (const gchar*) bb;
5807 gint result = strcmp ( namea, nameb );
5809 if ( result == 0 ) {
5810 // Found two names the same
5811 same_waypoint_name_udata *user_data = udata;
5812 user_data->has_same_waypoint_name = TRUE;
5813 user_data->same_waypoint_name = namea;
5816 // Leave ordering the same
5821 * Find out if any waypoints have the same name in this layer
5823 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5825 // Sort items by name, then compare if any next to each other are the same
5827 GList *waypoint_names = NULL;
5828 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5831 if ( ! waypoint_names )
5834 same_waypoint_name_udata udata;
5835 udata.has_same_waypoint_name = FALSE;
5837 // Use sort routine to traverse list comparing items
5838 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5839 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5840 // Still no waypoints...
5844 return udata.has_same_waypoint_name;
5848 * Force unqiue waypoint names for this layer
5849 * Note the panel is a required parameter to enable the update of the names displayed
5851 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5853 // . Search list for an instance of repeated name
5854 // . get waypoint of this name
5855 // . create new name
5856 // . rename waypoint & update equiv. treeview iter
5857 // . repeat until all different
5859 same_waypoint_name_udata udata;
5861 GList *waypoint_names = NULL;
5862 udata.has_same_waypoint_name = FALSE;
5863 udata.same_waypoint_name = NULL;
5865 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5868 if ( ! waypoint_names )
5871 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5873 // Still no waypoints...
5874 if ( ! dummy_list1 )
5877 while ( udata.has_same_waypoint_name ) {
5879 // Find a waypoint with the same name
5880 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5884 g_critical("Houston, we've had a problem.");
5885 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5886 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5891 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5892 vik_waypoint_set_name ( waypoint, newname );
5895 udataU.wp = waypoint;
5898 // Need want key of it for treeview update
5899 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5901 if ( wpf && udataU.uuid ) {
5903 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5906 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5907 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5908 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5913 // Start trying to find same names again...
5914 waypoint_names = NULL;
5915 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5916 udata.has_same_waypoint_name = FALSE;
5917 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5919 // No waypoints any more - give up searching
5920 if ( ! dummy_list2 )
5921 udata.has_same_waypoint_name = FALSE;
5925 vik_layers_panel_emit_update ( vlp );
5931 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5933 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5936 // Ensure list of waypoint names offered is unique
5937 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5938 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5939 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5940 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5946 // Sort list alphabetically for better presentation
5947 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5949 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5953 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5955 // Get list of items to delete from the user
5956 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5959 _("Delete Selection"),
5960 _("Select waypoints to delete"));
5963 // Delete requested waypoints
5964 // since specificly requested, IMHO no need for extra confirmation
5965 if ( delete_list ) {
5967 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5968 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5969 trw_layer_delete_waypoint_by_name (vtl, l->data);
5971 g_list_free(delete_list);
5973 trw_layer_calculate_bounds_waypoints ( vtl );
5974 vik_layer_emit_update( VIK_LAYER(vtl) );
5979 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5981 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5983 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5986 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5988 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5991 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
5992 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5996 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
5998 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6001 if ( !strncmp(wp->comment, "http", 4) ) {
6002 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6003 } else if ( !strncmp(wp->description, "http", 4) ) {
6004 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6008 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6010 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6012 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6014 // No actual change to the name supplied
6016 if (strcmp(newname, wp->name) == 0 )
6019 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6022 // An existing waypoint has been found with the requested name
6023 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6024 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6029 // Update WP name and refresh the treeview
6030 vik_waypoint_set_name (wp, newname);
6032 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6033 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6036 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6041 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6043 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6045 // No actual change to the name supplied
6047 if (strcmp(newname, trk->name) == 0)
6050 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6053 // An existing track has been found with the requested name
6054 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6055 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6059 // Update track name and refresh GUI parts
6060 vik_track_set_name (trk, newname);
6062 // Update any subwindows that could be displaying this track which has changed name
6063 // Only one Track Edit Window
6064 if ( l->current_tp_track == trk && l->tpwin ) {
6065 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6067 // Property Dialog of the track
6068 vik_trw_layer_propwin_update ( trk );
6070 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6071 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6074 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6079 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6081 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6083 // No actual change to the name supplied
6085 if (strcmp(newname, trk->name) == 0)
6088 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6091 // An existing track has been found with the requested name
6092 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6093 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6097 // Update track name and refresh GUI parts
6098 vik_track_set_name (trk, newname);
6100 // Update any subwindows that could be displaying this track which has changed name
6101 // Only one Track Edit Window
6102 if ( l->current_tp_track == trk && l->tpwin ) {
6103 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6105 // Property Dialog of the track
6106 vik_trw_layer_propwin_update ( trk );
6108 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6109 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6112 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6119 static gboolean is_valid_geocache_name ( gchar *str )
6121 gint len = strlen ( str );
6122 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]));
6125 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6127 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6128 a_acquire_set_filter_track ( trk );
6131 #ifdef VIK_CONFIG_GOOGLE
6132 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6134 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6135 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6138 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6140 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6142 gchar *escaped = uri_escape ( tr->comment );
6143 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6144 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6151 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6152 /* viewpoint is now available instead */
6153 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6155 static gpointer pass_along[8];
6157 gboolean rv = FALSE;
6160 pass_along[1] = vlp;
6161 pass_along[2] = GINT_TO_POINTER (subtype);
6162 pass_along[3] = sublayer;
6163 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6164 pass_along[5] = vvp;
6165 pass_along[6] = iter;
6166 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6168 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6172 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6174 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6175 gtk_widget_show ( item );
6177 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6178 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6179 if (tr && tr->property_dialog)
6180 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6182 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6183 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6184 if (tr && tr->property_dialog)
6185 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6188 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6189 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6190 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6191 gtk_widget_show ( item );
6193 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6194 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6195 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6196 gtk_widget_show ( item );
6198 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6199 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6200 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6201 gtk_widget_show ( item );
6203 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6205 gboolean separator_created = FALSE;
6207 /* could be a right-click using the tool */
6208 if ( vlp != NULL ) {
6209 item = gtk_menu_item_new ();
6210 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6211 gtk_widget_show ( item );
6213 separator_created = TRUE;
6215 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6216 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6217 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6218 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6219 gtk_widget_show ( item );
6222 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6224 if ( wp && wp->name ) {
6225 if ( is_valid_geocache_name ( wp->name ) ) {
6227 if ( !separator_created ) {
6228 item = gtk_menu_item_new ();
6229 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6230 gtk_widget_show ( item );
6231 separator_created = TRUE;
6234 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6235 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6236 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6237 gtk_widget_show ( item );
6241 if ( wp && wp->image )
6243 if ( !separator_created ) {
6244 item = gtk_menu_item_new ();
6245 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6246 gtk_widget_show ( item );
6247 separator_created = TRUE;
6250 // Set up image paramater
6251 pass_along[5] = wp->image;
6253 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6254 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
6255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6257 gtk_widget_show ( item );
6259 #ifdef VIK_CONFIG_GEOTAG
6260 GtkWidget *geotag_submenu = gtk_menu_new ();
6261 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6262 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6263 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6264 gtk_widget_show ( item );
6265 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6267 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6268 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6269 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6270 gtk_widget_show ( item );
6272 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6273 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6274 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6275 gtk_widget_show ( item );
6281 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6282 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6283 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6284 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6285 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6286 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6287 gtk_widget_show ( item );
6294 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6295 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6297 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6298 gtk_widget_show ( item );
6299 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6300 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6301 gtk_widget_set_sensitive ( item, TRUE );
6303 gtk_widget_set_sensitive ( item, FALSE );
6306 item = gtk_menu_item_new ();
6307 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6308 gtk_widget_show ( item );
6311 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6314 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6315 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6316 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6317 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6318 gtk_widget_show ( item );
6321 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6323 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6324 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6325 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6326 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6327 gtk_widget_show ( item );
6329 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6330 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6331 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6332 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6333 gtk_widget_show ( item );
6335 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6336 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6337 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6338 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6339 gtk_widget_show ( item );
6341 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6342 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6343 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6344 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6345 gtk_widget_show ( item );
6348 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6352 if ( l->current_track && !l->current_track->is_route ) {
6353 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6354 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6355 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6356 gtk_widget_show ( item );
6358 item = gtk_menu_item_new ();
6359 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6360 gtk_widget_show ( item );
6363 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6364 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6365 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6366 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6367 gtk_widget_show ( item );
6369 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6370 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6372 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6373 gtk_widget_show ( item );
6374 // Make it available only when a new track *not* already in progress
6375 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6377 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6378 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), 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 ( _("_Delete Tracks From Selection...") );
6384 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6386 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6387 gtk_widget_show ( item );
6390 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6394 if ( l->current_track && l->current_track->is_route ) {
6395 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6396 // Reuse finish track method
6397 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6398 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6399 gtk_widget_show ( item );
6401 item = gtk_menu_item_new ();
6402 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6403 gtk_widget_show ( item );
6406 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6407 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6408 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6409 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6410 gtk_widget_show ( item );
6412 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6413 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6414 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6415 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6416 gtk_widget_show ( item );
6417 // Make it available only when a new track *not* already in progress
6418 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6420 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6421 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6423 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6424 gtk_widget_show ( item );
6426 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6427 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6428 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6429 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6430 gtk_widget_show ( item );
6433 GtkWidget *upload_submenu = gtk_menu_new ();
6435 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6437 item = gtk_menu_item_new ();
6438 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6439 gtk_widget_show ( item );
6441 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6442 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6443 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6444 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6445 if ( l->current_track ) {
6446 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6447 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6448 gtk_widget_show ( item );
6451 item = gtk_menu_item_new ();
6452 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6453 gtk_widget_show ( item );
6456 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6457 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6459 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6460 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6461 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6462 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6463 gtk_widget_show ( item );
6465 GtkWidget *goto_submenu;
6466 goto_submenu = gtk_menu_new ();
6467 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6468 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6469 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6470 gtk_widget_show ( item );
6471 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6473 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6474 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6475 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6476 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6477 gtk_widget_show ( item );
6479 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6480 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6481 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6482 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6483 gtk_widget_show ( item );
6485 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6486 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6487 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6488 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6489 gtk_widget_show ( item );
6491 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6492 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6493 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6494 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6495 gtk_widget_show ( item );
6497 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6498 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6499 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6500 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6501 gtk_widget_show ( item );
6503 // Routes don't have speeds
6504 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6505 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6506 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6507 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6508 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6509 gtk_widget_show ( item );
6512 GtkWidget *combine_submenu;
6513 combine_submenu = gtk_menu_new ();
6514 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6515 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6516 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6517 gtk_widget_show ( item );
6518 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6520 // Routes don't have times or segments...
6521 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6522 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6524 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6525 gtk_widget_show ( item );
6527 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6529 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6530 gtk_widget_show ( item );
6533 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6535 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6536 gtk_widget_show ( item );
6538 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6539 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6541 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6542 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6543 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6544 gtk_widget_show ( item );
6546 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6547 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6549 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6550 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6551 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6552 gtk_widget_show ( item );
6554 GtkWidget *split_submenu;
6555 split_submenu = gtk_menu_new ();
6556 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6557 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6558 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6559 gtk_widget_show ( item );
6560 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6562 // Routes don't have times or segments...
6563 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6564 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6566 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6567 gtk_widget_show ( item );
6569 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6570 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6572 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6573 gtk_widget_show ( item );
6576 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6577 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6578 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6579 gtk_widget_show ( item );
6581 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6582 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6583 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6584 gtk_widget_show ( item );
6585 // Make it available only when a trackpoint is selected.
6586 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6588 GtkWidget *delete_submenu;
6589 delete_submenu = gtk_menu_new ();
6590 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6591 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6592 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6593 gtk_widget_show ( item );
6594 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6596 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6597 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6598 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6599 gtk_widget_show ( item );
6601 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6602 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6603 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6604 gtk_widget_show ( item );
6606 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6607 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6609 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6610 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6612 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6613 gtk_widget_show ( item );
6615 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6617 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6618 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6620 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6621 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
6622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6623 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6624 gtk_widget_show ( item );
6627 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6628 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
6629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6630 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6631 gtk_widget_show ( item );
6633 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6634 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6636 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6637 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6638 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6639 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6640 gtk_widget_show ( item );
6642 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6643 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6645 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6646 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6647 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6648 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6649 gtk_widget_show ( item );
6651 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6652 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6654 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6655 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6657 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6658 gtk_widget_show ( item );
6660 #ifdef VIK_CONFIG_GOOGLE
6661 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6662 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6663 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
6664 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6665 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6666 gtk_widget_show ( item );
6670 // ATM can't upload a single waypoint but can do waypoints to a GPS
6671 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6672 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6673 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6674 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6675 gtk_widget_show ( item );
6676 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6678 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6679 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6680 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6681 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6682 gtk_widget_show ( item );
6686 #ifdef VIK_CONFIG_GOOGLE
6687 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6689 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6690 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6691 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6692 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6693 gtk_widget_show ( item );
6697 // Some things aren't usable with routes
6698 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6699 #ifdef VIK_CONFIG_OPENSTREETMAP
6700 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6701 // Convert internal pointer into actual track for usage outside this file
6702 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6703 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6705 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6706 gtk_widget_show ( item );
6709 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6710 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6711 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6712 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6713 gtk_widget_show ( item );
6715 /* ATM This function is only available via the layers panel, due to needing a vlp */
6717 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6718 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6719 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6721 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6722 gtk_widget_show ( item );
6726 #ifdef VIK_CONFIG_GEOTAG
6727 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6728 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6729 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6730 gtk_widget_show ( item );
6734 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6735 // Only show on viewport popmenu when a trackpoint is selected
6736 if ( ! vlp && l->current_tpl ) {
6738 item = gtk_menu_item_new ();
6739 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6740 gtk_widget_show ( item );
6742 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6743 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6744 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6745 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6746 gtk_widget_show ( item );
6753 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6756 if (!vtl->current_tpl)
6758 if (!vtl->current_tpl->next)
6761 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6762 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6764 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6767 VikTrackpoint *tp_new = vik_trackpoint_new();
6768 struct LatLon ll_current, ll_next;
6769 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6770 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6772 /* main positional interpolation */
6773 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6774 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6776 /* Now other properties that can be interpolated */
6777 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6779 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6780 /* Note here the division is applied to each part, then added
6781 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6782 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6783 tp_new->has_timestamp = TRUE;
6786 if (tp_current->speed != NAN && tp_next->speed != NAN)
6787 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6789 /* TODO - improve interpolation of course, as it may not be correct.
6790 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6791 [similar applies if value is in radians] */
6792 if (tp_current->course != NAN && tp_next->course != NAN)
6793 tp_new->speed = (tp_current->course + tp_next->course)/2;
6795 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6797 /* Insert new point into the trackpoints list after the current TP */
6798 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6800 // Otherwise try routes
6801 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6805 gint index = g_list_index ( trk->trackpoints, tp_current );
6807 // NB no recalculation of bounds since it is inserted between points
6808 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6813 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6819 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6823 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6825 if ( vtl->current_tpl )
6827 vtl->current_tpl = NULL;
6828 vtl->current_tp_track = NULL;
6829 vtl->current_tp_id = NULL;
6830 vik_layer_emit_update(VIK_LAYER(vtl));
6834 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6836 g_assert ( vtl->tpwin != NULL );
6837 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6838 trw_layer_cancel_current_tp ( vtl, TRUE );
6840 if ( vtl->current_tpl == NULL )
6843 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6845 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6846 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6848 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6850 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6852 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6858 // Find available adjacent trackpoint
6859 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6861 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6862 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6864 // Delete current trackpoint
6865 vik_trackpoint_free ( vtl->current_tpl->data );
6866 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6868 // Set to current to the available adjacent trackpoint
6869 vtl->current_tpl = new_tpl;
6871 // Reset dialog with the available adjacent trackpoint
6872 if ( vtl->current_tp_track ) {
6873 vik_track_calculate_bounds ( vtl->current_tp_track );
6874 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6877 vik_layer_emit_update(VIK_LAYER(vtl));
6881 // Delete current trackpoint
6882 vik_trackpoint_free ( vtl->current_tpl->data );
6883 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6884 trw_layer_cancel_current_tp ( vtl, FALSE );
6887 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6889 if ( vtl->current_tp_track )
6890 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6891 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
6893 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6895 if ( vtl->current_tp_track )
6896 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6897 vik_layer_emit_update(VIK_LAYER(vtl));
6899 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6901 trw_layer_insert_tp_after_current_tp ( vtl );
6902 vik_layer_emit_update(VIK_LAYER(vtl));
6904 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6905 vik_layer_emit_update(VIK_LAYER(vtl));
6909 * trw_layer_dialog_shift:
6910 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
6912 * Try to reposition a dialog if it's over the specified coord
6913 * so to not obscure the item of interest
6915 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
6917 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
6919 // get parent window position & size
6920 gint win_pos_x, win_pos_y;
6921 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
6923 gint win_size_x, win_size_y;
6924 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
6926 // get own dialog size
6927 gint dia_size_x, dia_size_y;
6928 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
6930 // get own dialog position
6931 gint dia_pos_x, dia_pos_y;
6932 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
6934 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
6935 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
6937 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
6939 gint vp_xx, vp_yy; // In viewport pixels
6940 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
6942 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
6946 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
6948 // Transform Viewport pixels into absolute pixels
6949 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
6950 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
6952 // Is dialog over the point (to within an ^^ edge value)
6953 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
6954 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
6958 gint hh = vik_viewport_get_height ( vvp );
6960 // Consider the difference in viewport to the full window
6961 gint offset_y = dest_y;
6962 // Add difference between dialog and window sizes
6963 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
6965 if ( vp_yy > hh/2 ) {
6966 // Point in bottom half, move window to top half
6967 gtk_window_move ( dialog, dia_pos_x, offset_y );
6970 // Point in top half, move dialog down
6971 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
6975 // Shift left<->right
6976 gint ww = vik_viewport_get_width ( vvp );
6978 // Consider the difference in viewport to the full window
6979 gint offset_x = dest_x;
6980 // Add difference between dialog and window sizes
6981 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
6983 if ( vp_xx > ww/2 ) {
6984 // Point on right, move window to left
6985 gtk_window_move ( dialog, offset_x, dia_pos_y );
6988 // Point on left, move right
6989 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
6997 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7001 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7002 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7003 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7004 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7006 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7008 if ( vtl->current_tpl ) {
7009 // get tp pixel position
7010 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7012 // Shift up<->down to try not to obscure the trackpoint.
7013 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7017 if ( vtl->current_tpl )
7018 if ( vtl->current_tp_track )
7019 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7020 /* set layer name and TP data */
7023 /***************************************************************************
7025 ***************************************************************************/
7027 /*** Utility data structures and functions ****/
7031 gint closest_x, closest_y;
7032 gboolean draw_images;
7033 gpointer *closest_wp_id;
7034 VikWaypoint *closest_wp;
7040 gint closest_x, closest_y;
7041 gpointer closest_track_id;
7042 VikTrackpoint *closest_tp;
7048 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7054 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7056 // If waypoint has an image then use the image size to select
7057 if ( params->draw_images && wp->image ) {
7058 gint slackx, slacky;
7059 slackx = wp->image_width / 2;
7060 slacky = wp->image_height / 2;
7062 if ( x <= params->x + slackx && x >= params->x - slackx
7063 && y <= params->y + slacky && y >= params->y - slacky ) {
7064 params->closest_wp_id = id;
7065 params->closest_wp = wp;
7066 params->closest_x = x;
7067 params->closest_y = y;
7070 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7071 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7072 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7074 params->closest_wp_id = id;
7075 params->closest_wp = wp;
7076 params->closest_x = x;
7077 params->closest_y = y;
7081 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7083 GList *tpl = t->trackpoints;
7089 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7095 tp = VIK_TRACKPOINT(tpl->data);
7097 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7099 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7100 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7101 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7103 params->closest_track_id = id;
7104 params->closest_tp = tp;
7105 params->closest_tpl = tpl;
7106 params->closest_x = x;
7107 params->closest_y = y;
7113 // ATM: Leave this as 'Track' only.
7114 // Not overly bothered about having a snap to route trackpoint capability
7115 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7117 TPSearchParams params;
7121 params.closest_track_id = NULL;
7122 params.closest_tp = NULL;
7123 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7124 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7125 return params.closest_tp;
7128 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7130 WPSearchParams params;
7134 params.draw_images = vtl->drawimages;
7135 params.closest_wp = NULL;
7136 params.closest_wp_id = NULL;
7137 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7138 return params.closest_wp;
7142 // Some forward declarations
7143 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7144 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7145 static void marker_end_move ( tool_ed_t *t );
7148 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7152 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7154 // Here always allow snapping back to the original location
7155 // this is useful when one decides not to move the thing afterall
7156 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7159 if ( event->state & GDK_CONTROL_MASK )
7161 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7163 new_coord = tp->coord;
7167 if ( event->state & GDK_SHIFT_MASK )
7169 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7171 new_coord = wp->coord;
7175 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7177 marker_moveto ( t, x, y );
7184 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7186 if ( t->holding && event->button == 1 )
7189 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7192 if ( event->state & GDK_CONTROL_MASK )
7194 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7196 new_coord = tp->coord;
7200 if ( event->state & GDK_SHIFT_MASK )
7202 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7204 new_coord = wp->coord;
7207 marker_end_move ( t );
7209 // Determine if working on a waypoint or a trackpoint
7210 if ( t->is_waypoint ) {
7211 vtl->current_wp->coord = new_coord;
7212 trw_layer_calculate_bounds_waypoints ( vtl );
7215 if ( vtl->current_tpl ) {
7216 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7218 if ( vtl->current_tp_track )
7219 vik_track_calculate_bounds ( vtl->current_tp_track );
7222 if ( vtl->current_tp_track )
7223 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7228 vtl->current_wp = NULL;
7229 vtl->current_wp_id = NULL;
7230 trw_layer_cancel_current_tp ( vtl, FALSE );
7232 vik_layer_emit_update ( VIK_LAYER(vtl) );
7239 Returns true if a waypoint or track is found near the requested event position for this particular layer
7240 The item found is automatically selected
7241 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7243 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7245 if ( event->button != 1 )
7248 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7251 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7255 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7257 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7259 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7260 WPSearchParams wp_params;
7261 wp_params.vvp = vvp;
7262 wp_params.x = event->x;
7263 wp_params.y = event->y;
7264 wp_params.draw_images = vtl->drawimages;
7265 wp_params.closest_wp_id = NULL;
7266 wp_params.closest_wp = NULL;
7268 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7270 if ( wp_params.closest_wp ) {
7273 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7275 // Too easy to move it so must be holding shift to start immediately moving it
7276 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7277 if ( event->state & GDK_SHIFT_MASK ||
7278 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7279 // Put into 'move buffer'
7280 // NB vvp & vw already set in tet
7281 tet->vtl = (gpointer)vtl;
7282 tet->is_waypoint = TRUE;
7284 marker_begin_move (tet, event->x, event->y);
7287 vtl->current_wp = wp_params.closest_wp;
7288 vtl->current_wp_id = wp_params.closest_wp_id;
7290 vik_layer_emit_update ( VIK_LAYER(vtl) );
7296 // Used for both track and route lists
7297 TPSearchParams tp_params;
7298 tp_params.vvp = vvp;
7299 tp_params.x = event->x;
7300 tp_params.y = event->y;
7301 tp_params.closest_track_id = NULL;
7302 tp_params.closest_tp = NULL;
7303 tp_params.bbox = bbox;
7305 if (vtl->tracks_visible) {
7306 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7308 if ( tp_params.closest_tp ) {
7310 // Always select + highlight the track
7311 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7313 tet->is_waypoint = FALSE;
7315 // Select the Trackpoint
7316 // Can move it immediately when control held or it's the previously selected tp
7317 if ( event->state & GDK_CONTROL_MASK ||
7318 vtl->current_tpl == tp_params.closest_tpl ) {
7319 // Put into 'move buffer'
7320 // NB vvp & vw already set in tet
7321 tet->vtl = (gpointer)vtl;
7322 marker_begin_move (tet, event->x, event->y);
7325 vtl->current_tpl = tp_params.closest_tpl;
7326 vtl->current_tp_id = tp_params.closest_track_id;
7327 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7329 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7332 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7334 vik_layer_emit_update ( VIK_LAYER(vtl) );
7339 // Try again for routes
7340 if (vtl->routes_visible) {
7341 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7343 if ( tp_params.closest_tp ) {
7345 // Always select + highlight the track
7346 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7348 tet->is_waypoint = FALSE;
7350 // Select the Trackpoint
7351 // Can move it immediately when control held or it's the previously selected tp
7352 if ( event->state & GDK_CONTROL_MASK ||
7353 vtl->current_tpl == tp_params.closest_tpl ) {
7354 // Put into 'move buffer'
7355 // NB vvp & vw already set in tet
7356 tet->vtl = (gpointer)vtl;
7357 marker_begin_move (tet, event->x, event->y);
7360 vtl->current_tpl = tp_params.closest_tpl;
7361 vtl->current_tp_id = tp_params.closest_track_id;
7362 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7364 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7367 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7369 vik_layer_emit_update ( VIK_LAYER(vtl) );
7374 /* these aren't the droids you're looking for */
7375 vtl->current_wp = NULL;
7376 vtl->current_wp_id = NULL;
7377 trw_layer_cancel_current_tp ( vtl, FALSE );
7380 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7385 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7387 if ( event->button != 3 )
7390 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7393 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7396 /* Post menu for the currently selected item */
7398 /* See if a track is selected */
7399 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7400 if ( track && track->visible ) {
7402 if ( track->name ) {
7404 if ( vtl->track_right_click_menu )
7405 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
7407 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7414 if ( track->is_route )
7415 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7417 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7419 if ( trkf && udataU.uuid ) {
7422 if ( track->is_route )
7423 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7425 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7427 trw_layer_sublayer_add_menu_items ( vtl,
7428 vtl->track_right_click_menu,
7430 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7436 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7442 /* See if a waypoint is selected */
7443 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7444 if ( waypoint && waypoint->visible ) {
7445 if ( waypoint->name ) {
7447 if ( vtl->wp_right_click_menu )
7448 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7450 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7453 udata.wp = waypoint;
7456 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7458 if ( wpf && udata.uuid ) {
7459 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7461 trw_layer_sublayer_add_menu_items ( vtl,
7462 vtl->wp_right_click_menu,
7464 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7469 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7478 /* background drawing hook, to be passed the viewport */
7479 static gboolean tool_sync_done = TRUE;
7481 static gboolean tool_sync(gpointer data)
7483 VikViewport *vvp = data;
7484 gdk_threads_enter();
7485 vik_viewport_sync(vvp);
7486 tool_sync_done = TRUE;
7487 gdk_threads_leave();
7491 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7494 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7495 gdk_gc_set_function ( t->gc, GDK_INVERT );
7496 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7497 vik_viewport_sync(t->vvp);
7502 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7504 VikViewport *vvp = t->vvp;
7505 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7506 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7510 if (tool_sync_done) {
7511 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7512 tool_sync_done = FALSE;
7516 static void marker_end_move ( tool_ed_t *t )
7518 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7519 g_object_unref ( t->gc );
7523 /*** Edit waypoint ****/
7525 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7527 tool_ed_t *t = g_new(tool_ed_t, 1);
7533 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7535 WPSearchParams params;
7536 tool_ed_t *t = data;
7537 VikViewport *vvp = t->vvp;
7539 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7546 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7549 if ( vtl->current_wp && vtl->current_wp->visible )
7551 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7553 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7555 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7556 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7558 if ( event->button == 3 )
7559 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7561 marker_begin_move(t, event->x, event->y);
7568 params.x = event->x;
7569 params.y = event->y;
7570 params.draw_images = vtl->drawimages;
7571 params.closest_wp_id = NULL;
7572 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7573 params.closest_wp = NULL;
7574 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7575 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7577 // how do we get here?
7578 marker_begin_move(t, event->x, event->y);
7579 g_critical("shouldn't be here");
7582 else if ( params.closest_wp )
7584 if ( event->button == 3 )
7585 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7587 vtl->waypoint_rightclick = FALSE;
7589 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7591 vtl->current_wp = params.closest_wp;
7592 vtl->current_wp_id = params.closest_wp_id;
7594 /* could make it so don't update if old WP is off screen and new is null but oh well */
7595 vik_layer_emit_update ( VIK_LAYER(vtl) );
7599 vtl->current_wp = NULL;
7600 vtl->current_wp_id = NULL;
7601 vtl->waypoint_rightclick = FALSE;
7602 vik_layer_emit_update ( VIK_LAYER(vtl) );
7606 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7608 tool_ed_t *t = data;
7609 VikViewport *vvp = t->vvp;
7611 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7616 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7619 if ( event->state & GDK_CONTROL_MASK )
7621 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7623 new_coord = tp->coord;
7627 if ( event->state & GDK_SHIFT_MASK )
7629 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7630 if ( wp && wp != vtl->current_wp )
7631 new_coord = wp->coord;
7636 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7638 marker_moveto ( t, x, y );
7645 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7647 tool_ed_t *t = data;
7648 VikViewport *vvp = t->vvp;
7650 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7653 if ( t->holding && event->button == 1 )
7656 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7659 if ( event->state & GDK_CONTROL_MASK )
7661 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7663 new_coord = tp->coord;
7667 if ( event->state & GDK_SHIFT_MASK )
7669 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7670 if ( wp && wp != vtl->current_wp )
7671 new_coord = wp->coord;
7674 marker_end_move ( t );
7676 vtl->current_wp->coord = new_coord;
7678 trw_layer_calculate_bounds_waypoints ( vtl );
7679 vik_layer_emit_update ( VIK_LAYER(vtl) );
7682 /* PUT IN RIGHT PLACE!!! */
7683 if ( event->button == 3 && vtl->waypoint_rightclick )
7685 if ( vtl->wp_right_click_menu )
7686 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7687 if ( vtl->current_wp ) {
7688 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7689 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 );
7690 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7692 vtl->waypoint_rightclick = FALSE;
7697 /*** New track ****/
7699 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7706 GdkDrawable *drawable;
7712 * Draw specified pixmap
7714 static gboolean draw_sync ( gpointer data )
7716 draw_sync_t *ds = (draw_sync_t*) data;
7717 // Sometimes don't want to draw
7718 // normally because another update has taken precedent such as panning the display
7719 // which means this pixmap is no longer valid
7720 if ( ds->vtl->draw_sync_do ) {
7721 gdk_threads_enter();
7722 gdk_draw_drawable (ds->drawable,
7725 0, 0, 0, 0, -1, -1);
7726 ds->vtl->draw_sync_done = TRUE;
7727 gdk_threads_leave();
7732 static gchar* distance_string (gdouble distance)
7736 /* draw label with distance */
7737 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7738 switch (dist_units) {
7739 case VIK_UNITS_DISTANCE_MILES:
7740 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7741 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7742 } else if (distance < 1609.4) {
7743 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7745 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7749 // VIK_UNITS_DISTANCE_KILOMETRES
7750 if (distance >= 1000 && distance < 100000) {
7751 g_sprintf(str, "%3.2f km", distance/1000.0);
7752 } else if (distance < 1000) {
7753 g_sprintf(str, "%d m", (int)distance);
7755 g_sprintf(str, "%d km", (int)distance/1000);
7759 return g_strdup (str);
7763 * Actually set the message in statusbar
7765 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
7767 // Only show elevation data when track has some elevation properties
7768 gchar str_gain_loss[64];
7769 str_gain_loss[0] = '\0';
7770 gchar str_last_step[64];
7771 str_last_step[0] = '\0';
7772 gchar *str_total = distance_string (distance);
7774 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7775 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7776 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7778 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7781 if ( last_step > 0 ) {
7782 gchar *tmp = distance_string (last_step);
7783 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
7787 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
7789 // Write with full gain/loss information
7790 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
7791 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
7793 g_free ( str_total );
7797 * Figure out what information should be set in the statusbar and then write it
7799 static void update_statusbar ( VikTrwLayer *vtl )
7801 // Get elevation data
7802 gdouble elev_gain, elev_loss;
7803 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7805 /* Find out actual distance of current track */
7806 gdouble distance = vik_track_get_length (vtl->current_track);
7808 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
7812 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7814 /* if we haven't sync'ed yet, we don't have time to do more. */
7815 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7816 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7817 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7819 static GdkPixmap *pixmap = NULL;
7821 // Need to check in case window has been resized
7822 w1 = vik_viewport_get_width(vvp);
7823 h1 = vik_viewport_get_height(vvp);
7825 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
7827 gdk_drawable_get_size (pixmap, &w2, &h2);
7828 if (w1 != w2 || h1 != h2) {
7829 g_object_unref ( G_OBJECT ( pixmap ) );
7830 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
7833 // Reset to background
7834 gdk_draw_drawable (pixmap,
7835 vtl->current_track_newpoint_gc,
7836 vik_viewport_get_pixmap(vvp),
7837 0, 0, 0, 0, -1, -1);
7839 draw_sync_t *passalong;
7842 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7844 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7845 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7846 // thus when we come to reset to the background it would include what we have already drawn!!
7847 gdk_draw_line ( pixmap,
7848 vtl->current_track_newpoint_gc,
7849 x1, y1, event->x, event->y );
7850 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7852 /* Find out actual distance of current track */
7853 gdouble distance = vik_track_get_length (vtl->current_track);
7855 // Now add distance to where the pointer is //
7858 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7859 vik_coord_to_latlon ( &coord, &ll );
7860 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
7861 distance = distance + last_step;
7863 // Get elevation data
7864 gdouble elev_gain, elev_loss;
7865 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7867 // Adjust elevation data (if available) for the current pointer position
7869 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7870 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7871 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7872 // Adjust elevation of last track point
7873 if ( elev_new > last_tpt->altitude )
7875 elev_gain += elev_new - last_tpt->altitude;
7878 elev_loss += last_tpt->altitude - elev_new;
7883 // Display of the distance 'tooltip' during track creation is controlled by a preference
7885 if ( a_vik_get_create_track_tooltip() ) {
7887 gchar *str = distance_string (distance);
7889 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7890 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
7891 pango_layout_set_text (pl, str, -1);
7893 pango_layout_get_pixel_size ( pl, &wd, &hd );
7896 // offset from cursor a bit depending on font size
7900 // Create a background block to make the text easier to read over the background map
7901 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7902 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7903 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7905 g_object_unref ( G_OBJECT ( pl ) );
7906 g_object_unref ( G_OBJECT ( background_block_gc ) );
7910 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7911 passalong->vtl = vtl;
7912 passalong->pixmap = pixmap;
7913 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
7914 passalong->gc = vtl->current_track_newpoint_gc;
7918 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
7920 // Update statusbar with full gain/loss information
7921 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7923 // draw pixmap when we have time to
7924 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7925 vtl->draw_sync_done = FALSE;
7926 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7928 return VIK_LAYER_TOOL_ACK;
7931 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7933 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7934 vtl->current_track = NULL;
7935 vik_layer_emit_update ( VIK_LAYER(vtl) );
7937 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7939 if ( vtl->current_track->trackpoints )
7941 GList *last = g_list_last(vtl->current_track->trackpoints);
7942 g_free ( last->data );
7943 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7946 update_statusbar ( vtl );
7948 vik_layer_emit_update ( VIK_LAYER(vtl) );
7955 * Common function to handle trackpoint button requests on either a route or a track
7956 * . enables adding a point via normal click
7957 * . enables removal of last point via right click
7958 * . finishing of the track or route via double clicking
7960 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7964 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7967 if ( event->button == 2 ) {
7968 // As the display is panning, the new track pixmap is now invalid so don't draw it
7969 // otherwise this drawing done results in flickering back to an old image
7970 vtl->draw_sync_do = FALSE;
7974 if ( event->button == 3 )
7976 if ( !vtl->current_track )
7979 if ( vtl->current_track->trackpoints )
7981 GList *last = g_list_last(vtl->current_track->trackpoints);
7982 g_free ( last->data );
7983 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7985 vik_track_calculate_bounds ( vtl->current_track );
7986 update_statusbar ( vtl );
7988 vik_layer_emit_update ( VIK_LAYER(vtl) );
7992 if ( event->type == GDK_2BUTTON_PRESS )
7994 /* subtract last (duplicate from double click) tp then end */
7995 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7997 GList *last = g_list_last(vtl->current_track->trackpoints);
7998 g_free ( last->data );
7999 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8000 /* undo last, then end */
8001 vtl->current_track = NULL;
8003 vik_layer_emit_update ( VIK_LAYER(vtl) );
8007 tp = vik_trackpoint_new();
8008 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8010 /* snap to other TP */
8011 if ( event->state & GDK_CONTROL_MASK )
8013 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8015 tp->coord = other_tp->coord;
8018 tp->newsegment = FALSE;
8019 tp->has_timestamp = FALSE;
8022 if ( vtl->current_track ) {
8023 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
8024 /* Auto attempt to get elevation from DEM data (if it's available) */
8025 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8026 vik_track_calculate_bounds ( vtl->current_track );
8029 vtl->ct_x1 = vtl->ct_x2;
8030 vtl->ct_y1 = vtl->ct_y2;
8031 vtl->ct_x2 = event->x;
8032 vtl->ct_y2 = event->y;
8034 vik_layer_emit_update ( VIK_LAYER(vtl) );
8038 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8040 // ----------------------------------------------------- if current is a route - switch to new track
8041 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8043 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8044 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8046 new_track_create_common ( vtl, name );
8051 return tool_new_track_or_route_click ( vtl, event, vvp );
8054 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8056 if ( event->button == 2 ) {
8057 // Pan moving ended - enable potential point drawing again
8058 vtl->draw_sync_do = TRUE;
8059 vtl->draw_sync_done = TRUE;
8063 /*** New route ****/
8065 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8070 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8072 // -------------------------- if current is a track - switch to new route
8073 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8075 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8076 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
8077 new_route_create_common ( vtl, name );
8081 return tool_new_track_or_route_click ( vtl, event, vvp );
8084 /*** New waypoint ****/
8086 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8091 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8094 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8096 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8097 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8098 trw_layer_calculate_bounds_waypoints ( vtl );
8099 vik_layer_emit_update ( VIK_LAYER(vtl) );
8105 /*** Edit trackpoint ****/
8107 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8109 tool_ed_t *t = g_new(tool_ed_t, 1);
8115 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8117 tool_ed_t *t = data;
8118 VikViewport *vvp = t->vvp;
8119 TPSearchParams params;
8120 /* OUTDATED DOCUMENTATION:
8121 find 5 pixel range on each side. then put these UTM, and a pointer
8122 to the winning track name (and maybe the winning track itself), and a
8123 pointer to the winning trackpoint, inside an array or struct. pass
8124 this along, do a foreach on the tracks which will do a foreach on the
8127 params.x = event->x;
8128 params.y = event->y;
8129 params.closest_track_id = NULL;
8130 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8131 params.closest_tp = NULL;
8133 if ( event->button != 1 )
8136 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8139 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8142 if ( vtl->current_tpl )
8144 /* 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.) */
8145 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8146 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8151 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8153 if ( current_tr->visible &&
8154 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8155 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8156 marker_begin_move ( t, event->x, event->y );
8162 if ( vtl->tracks_visible )
8163 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8165 if ( params.closest_tp )
8167 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8168 vtl->current_tpl = params.closest_tpl;
8169 vtl->current_tp_id = params.closest_track_id;
8170 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8171 trw_layer_tpwin_init ( vtl );
8172 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8173 vik_layer_emit_update ( VIK_LAYER(vtl) );
8177 if ( vtl->routes_visible )
8178 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8180 if ( params.closest_tp )
8182 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8183 vtl->current_tpl = params.closest_tpl;
8184 vtl->current_tp_id = params.closest_track_id;
8185 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8186 trw_layer_tpwin_init ( vtl );
8187 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8188 vik_layer_emit_update ( VIK_LAYER(vtl) );
8192 /* these aren't the droids you're looking for */
8196 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8198 tool_ed_t *t = data;
8199 VikViewport *vvp = t->vvp;
8201 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8207 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8210 if ( event->state & GDK_CONTROL_MASK )
8212 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8213 if ( tp && tp != vtl->current_tpl->data )
8214 new_coord = tp->coord;
8216 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8219 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8220 marker_moveto ( t, x, y );
8228 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8230 tool_ed_t *t = data;
8231 VikViewport *vvp = t->vvp;
8233 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8235 if ( event->button != 1)
8240 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8243 if ( event->state & GDK_CONTROL_MASK )
8245 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8246 if ( tp && tp != vtl->current_tpl->data )
8247 new_coord = tp->coord;
8250 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8251 if ( vtl->current_tp_track )
8252 vik_track_calculate_bounds ( vtl->current_tp_track );
8254 marker_end_move ( t );
8256 /* diff dist is diff from orig */
8258 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8260 vik_layer_emit_update ( VIK_LAYER(vtl) );
8267 #ifdef VIK_CONFIG_GOOGLE
8268 /*** Route Finder ***/
8269 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8274 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8277 if ( !vtl ) return FALSE;
8278 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8279 if ( event->button == 3 && vtl->route_finder_current_track ) {
8281 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8283 vtl->route_finder_coord = *new_end;
8285 vik_layer_emit_update ( VIK_LAYER(vtl) );
8286 /* remove last ' to:...' */
8287 if ( vtl->route_finder_current_track->comment ) {
8288 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8289 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8290 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8291 last_to - vtl->route_finder_current_track->comment - 1);
8292 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8297 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8298 struct LatLon start, end;
8299 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
8300 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
8303 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8304 vik_coord_to_latlon ( &(tmp), &end );
8305 vtl->route_finder_coord = tmp; /* for continuations */
8307 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8308 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8309 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8311 vtl->route_finder_check_added_track = TRUE;
8312 vtl->route_finder_started = FALSE;
8315 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
8316 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
8317 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
8318 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
8319 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
8320 // NB normally this returns a GPX Route - so subsequent usage of it must lookup via the routes hash
8321 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL, NULL );
8324 /* see if anything was done -- a track was added or appended to */
8325 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8326 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 ) );
8327 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8328 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8329 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8330 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8333 if ( vtl->route_finder_added_track )
8334 vik_track_calculate_bounds ( vtl->route_finder_added_track );
8336 vtl->route_finder_added_track = NULL;
8337 vtl->route_finder_check_added_track = FALSE;
8338 vtl->route_finder_append = FALSE;
8340 vik_layer_emit_update ( VIK_LAYER(vtl) );
8342 vtl->route_finder_started = TRUE;
8343 vtl->route_finder_coord = tmp;
8344 vtl->route_finder_current_track = NULL;
8350 /*** Show picture ****/
8352 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8357 /* Params are: vvp, event, last match found or NULL */
8358 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8360 if ( wp->image && wp->visible )
8362 gint x, y, slackx, slacky;
8363 GdkEventButton *event = (GdkEventButton *) params[1];
8365 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8366 slackx = wp->image_width / 2;
8367 slacky = wp->image_height / 2;
8368 if ( x <= event->x + slackx && x >= event->x - slackx
8369 && y <= event->y + slacky && y >= event->y - slacky )
8371 params[2] = wp->image; /* we've found a match. however continue searching
8372 * since we want to find the last match -- that
8373 * is, the match that was drawn last. */
8378 static void trw_layer_show_picture ( gpointer pass_along[6] )
8380 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8382 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8385 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8386 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8387 g_free ( quoted_file );
8388 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8390 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() );
8391 g_error_free ( err );
8394 #endif /* WINDOWS */
8397 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8399 gpointer params[3] = { vvp, event, NULL };
8400 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8402 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8405 static gpointer pass_along[6];
8406 pass_along[0] = vtl;
8407 pass_along[5] = params[2];
8408 trw_layer_show_picture ( pass_along );
8409 return TRUE; /* found a match */
8412 return FALSE; /* go through other layers, searching for a match */
8415 /***************************************************************************
8417 ***************************************************************************/
8420 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8422 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8423 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8426 /* Structure for thumbnail creating data used in the background thread */
8428 VikTrwLayer *vtl; // Layer needed for redrawing
8429 GSList *pics; // Image list
8430 } thumbnail_create_thread_data;
8432 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8434 guint total = g_slist_length(tctd->pics), done = 0;
8435 while ( tctd->pics )
8437 a_thumbnails_create ( (gchar *) tctd->pics->data );
8438 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8440 return -1; /* Abort thread */
8442 tctd->pics = tctd->pics->next;
8445 // Redraw to show the thumbnails as they are now created
8446 if ( IS_VIK_LAYER(tctd->vtl) )
8447 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8452 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8454 while ( tctd->pics )
8456 g_free ( tctd->pics->data );
8457 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8462 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8464 if ( ! vtl->has_verified_thumbnails )
8466 GSList *pics = NULL;
8467 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8470 gint len = g_slist_length ( pics );
8471 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8472 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8475 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8477 (vik_thr_func) create_thumbnails_thread,
8479 (vik_thr_free_func) thumbnail_create_thread_free,
8487 static const gchar* my_track_colors ( gint ii )
8489 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8501 // Fast and reliable way of returning a colour
8502 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8505 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8507 GHashTableIter iter;
8508 gpointer key, value;
8512 g_hash_table_iter_init ( &iter, vtl->tracks );
8514 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8516 // Tracks get a random spread of colours if not already assigned
8517 if ( ! VIK_TRACK(value)->has_color ) {
8518 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8519 VIK_TRACK(value)->color = vtl->track_color;
8521 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8523 VIK_TRACK(value)->has_color = TRUE;
8526 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8529 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8535 g_hash_table_iter_init ( &iter, vtl->routes );
8537 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8539 // Routes get an intermix of reds
8540 if ( ! VIK_TRACK(value)->has_color ) {
8542 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8544 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8545 VIK_TRACK(value)->has_color = TRUE;
8548 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8555 * (Re)Calculate the bounds of the waypoints in this layer,
8556 * This should be called whenever waypoints are changed
8558 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
8560 struct LatLon topleft = { 0.0, 0.0 };
8561 struct LatLon bottomright = { 0.0, 0.0 };
8564 GHashTableIter iter;
8565 gpointer key, value;
8567 g_hash_table_iter_init ( &iter, vtl->waypoints );
8569 // Set bounds to first point
8570 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
8571 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
8572 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
8575 // Ensure there is another point...
8576 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
8578 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8580 // See if this point increases the bounds.
8581 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
8583 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
8584 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
8585 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
8586 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
8590 vtl->waypoints_bbox.north = topleft.lat;
8591 vtl->waypoints_bbox.east = bottomright.lon;
8592 vtl->waypoints_bbox.south = bottomright.lat;
8593 vtl->waypoints_bbox.west = topleft.lon;
8596 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
8598 vik_track_calculate_bounds ( trk );
8601 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
8603 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8604 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8607 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
8609 trw_layer_verify_thumbnails ( vtl, vvp );
8610 trw_layer_track_alloc_colors ( vtl );
8612 trw_layer_calculate_bounds_waypoints ( vtl );
8613 trw_layer_calculate_bounds_tracks ( vtl );
8616 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8618 return vtl->coord_mode;
8622 * Uniquify the whole layer
8623 * Also requires the layers panel as the names shown there need updating too
8624 * Returns whether the operation was successful or not
8626 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8629 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8630 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8631 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8637 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8639 vik_coord_convert ( &(wp->coord), *dest_mode );
8642 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
8644 vik_track_convert ( tr, *dest_mode );
8647 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8649 if ( vtl->coord_mode != dest_mode )
8651 vtl->coord_mode = dest_mode;
8652 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8653 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8654 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
8658 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8660 vtl->menu_selection = selection;
8663 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8665 return (vtl->menu_selection);
8668 /* ----------- Downloading maps along tracks --------------- */
8670 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8672 /* TODO: calculating based on current size of viewport */
8673 const gdouble w_at_zoom_0_125 = 0.0013;
8674 const gdouble h_at_zoom_0_125 = 0.0011;
8675 gdouble zoom_factor = zoom_level/0.125;
8677 wh->lat = h_at_zoom_0_125 * zoom_factor;
8678 wh->lon = w_at_zoom_0_125 * zoom_factor;
8680 return 0; /* all OK */
8683 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8685 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8686 (dist->lat >= ABS(to->north_south - from->north_south)))
8689 VikCoord *coord = g_malloc(sizeof(VikCoord));
8690 coord->mode = VIK_COORD_LATLON;
8692 if (ABS(gradient) < 1) {
8693 if (from->east_west > to->east_west)
8694 coord->east_west = from->east_west - dist->lon;
8696 coord->east_west = from->east_west + dist->lon;
8697 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8699 if (from->north_south > to->north_south)
8700 coord->north_south = from->north_south - dist->lat;
8702 coord->north_south = from->north_south + dist->lat;
8703 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8709 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8711 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8712 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8714 VikCoord *next = from;
8716 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8718 list = g_list_prepend(list, next);
8724 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8726 typedef struct _Rect {
8731 #define GLRECT(iter) ((Rect *)((iter)->data))
8734 GList *rects_to_download = NULL;
8737 if (get_download_area_width(vvp, zoom_level, &wh))
8740 GList *iter = tr->trackpoints;
8744 gboolean new_map = TRUE;
8745 VikCoord *cur_coord, tl, br;
8748 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8750 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8751 rect = g_malloc(sizeof(Rect));
8754 rect->center = *cur_coord;
8755 rects_to_download = g_list_prepend(rects_to_download, rect);
8760 gboolean found = FALSE;
8761 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8762 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8773 GList *fillins = NULL;
8774 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8775 /* seems that ATM the function get_next_coord works only for LATLON */
8776 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8777 /* fill-ins for far apart points */
8778 GList *cur_rect, *next_rect;
8779 for (cur_rect = rects_to_download;
8780 (next_rect = cur_rect->next) != NULL;
8781 cur_rect = cur_rect->next) {
8782 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8783 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8784 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8788 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8791 GList *iter = fillins;
8793 cur_coord = (VikCoord *)(iter->data);
8794 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8795 rect = g_malloc(sizeof(Rect));
8798 rect->center = *cur_coord;
8799 rects_to_download = g_list_prepend(rects_to_download, rect);
8804 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8805 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8809 for (iter = fillins; iter; iter = iter->next)
8811 g_list_free(fillins);
8813 if (rects_to_download) {
8814 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8815 g_free(rect_iter->data);
8816 g_list_free(rects_to_download);
8820 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8823 gint selected_map, default_map;
8824 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8825 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8826 gint selected_zoom, default_zoom;
8830 VikTrwLayer *vtl = pass_along[0];
8831 VikLayersPanel *vlp = pass_along[1];
8833 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8834 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8836 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8840 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8842 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8843 int num_maps = g_list_length(vmls);
8846 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8850 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8851 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8853 gchar **np = map_names;
8854 VikMapsLayer **lp = map_layers;
8855 for (i = 0; i < num_maps; i++) {
8856 gboolean dup = FALSE;
8857 vml = (VikMapsLayer *)(vmls->data);
8858 for (j = 0; j < i; j++) { /* no duplicate allowed */
8859 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8866 *np++ = vik_maps_layer_get_map_label(vml);
8872 num_maps = lp - map_layers;
8874 for (default_map = 0; default_map < num_maps; default_map++) {
8875 /* TODO: check for parent layer's visibility */
8876 if (VIK_LAYER(map_layers[default_map])->visible)
8879 default_map = (default_map == num_maps) ? 0 : default_map;
8881 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8882 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8883 if (cur_zoom == zoom_vals[default_zoom])
8886 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8888 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8891 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8894 for (i = 0; i < num_maps; i++)
8895 g_free(map_names[i]);
8903 /**** lowest waypoint number calculation ***/
8904 static gint highest_wp_number_name_to_number(const gchar *name) {
8905 if ( strlen(name) == 3 ) {
8907 if ( n < 100 && name[0] != '0' )
8909 if ( n < 10 && name[0] != '0' )
8917 static void highest_wp_number_reset(VikTrwLayer *vtl)
8919 vtl->highest_wp_number = -1;
8922 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8924 /* if is bigger that top, add it */
8925 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8926 if ( new_wp_num > vtl->highest_wp_number )
8927 vtl->highest_wp_number = new_wp_num;
8930 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8932 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8933 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8934 if ( vtl->highest_wp_number == old_wp_num ) {
8936 vtl->highest_wp_number--;
8938 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8939 /* search down until we find something that *does* exist */
8941 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8942 vtl->highest_wp_number--;
8943 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8948 /* get lowest unused number */
8949 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8952 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8954 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8955 return g_strdup(buf);