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 // Recalculate bounds even if not renamed as maybe dragged between layers
3903 trw_layer_calculate_bounds_waypoints ( vtl_dest );
3904 trw_layer_calculate_bounds_waypoints ( vtl_src );
3908 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3910 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3911 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3913 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3914 GList *items = NULL;
3917 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3918 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3920 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3921 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3923 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3924 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3929 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3930 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3932 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3933 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3935 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3942 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3943 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3948 VikTrack *trk; // input
3949 gpointer uuid; // output
3952 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3954 trku_udata *user_data = udata;
3955 if ( trk == user_data->trk ) {
3956 user_data->uuid = id;
3962 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3964 gboolean was_visible = FALSE;
3966 if ( trk && trk->name ) {
3968 if ( trk == vtl->current_track ) {
3969 vtl->current_track = NULL;
3970 vtl->current_tp_track = NULL;
3971 vtl->current_tp_id = NULL;
3972 vtl->moving_tp = FALSE;
3975 was_visible = trk->visible;
3977 if ( trk == vtl->route_finder_current_track )
3978 vtl->route_finder_current_track = NULL;
3980 if ( trk == vtl->route_finder_added_track )
3981 vtl->route_finder_added_track = NULL;
3987 // Hmmm, want key of it
3988 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3990 if ( trkf && udata.uuid ) {
3991 /* could be current_tp, so we have to check */
3992 trw_layer_cancel_tps_of_track ( vtl, trk );
3994 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3997 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3998 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3999 g_hash_table_remove ( vtl->tracks, udata.uuid );
4001 // If last sublayer, then remove sublayer container
4002 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4003 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4011 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4013 gboolean was_visible = FALSE;
4015 if ( trk && trk->name ) {
4017 if ( trk == vtl->current_track ) {
4018 vtl->current_track = NULL;
4019 vtl->current_tp_track = NULL;
4020 vtl->current_tp_id = NULL;
4021 vtl->moving_tp = FALSE;
4024 was_visible = trk->visible;
4026 if ( trk == vtl->route_finder_current_track )
4027 vtl->route_finder_current_track = NULL;
4029 if ( trk == vtl->route_finder_added_track )
4030 vtl->route_finder_added_track = NULL;
4036 // Hmmm, want key of it
4037 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4039 if ( trkf && udata.uuid ) {
4040 /* could be current_tp, so we have to check */
4041 trw_layer_cancel_tps_of_track ( vtl, trk );
4043 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4046 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4047 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4048 g_hash_table_remove ( vtl->routes, udata.uuid );
4050 // If last sublayer, then remove sublayer container
4051 if ( g_hash_table_size (vtl->routes) == 0 ) {
4052 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4060 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4062 gboolean was_visible = FALSE;
4064 if ( wp && wp->name ) {
4066 if ( wp == vtl->current_wp ) {
4067 vtl->current_wp = NULL;
4068 vtl->current_wp_id = NULL;
4069 vtl->moving_wp = FALSE;
4072 was_visible = wp->visible;
4078 // Hmmm, want key of it
4079 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4081 if ( wpf && udata.uuid ) {
4082 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4085 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4086 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4088 highest_wp_number_remove_wp(vtl, wp->name);
4089 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4091 // If last sublayer, then remove sublayer container
4092 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4093 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4103 // Only for temporary use by trw_layer_delete_waypoint_by_name
4104 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4106 wpu_udata *user_data = udata;
4107 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4108 user_data->uuid = id;
4115 * Delete a waypoint by the given name
4116 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4117 * as there be multiple waypoints with the same name
4119 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4122 // Fake a waypoint with the given name
4123 udata.wp = vik_waypoint_new ();
4124 vik_waypoint_set_name (udata.wp, name);
4125 // Currently only the name is used in this waypoint find function
4128 // Hmmm, want key of it
4129 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4131 vik_waypoint_free (udata.wp);
4133 if ( wpf && udata.uuid )
4134 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4140 VikTrack *trk; // input
4141 gpointer uuid; // output
4144 // Only for temporary use by trw_layer_delete_track_by_name
4145 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4147 tpu_udata *user_data = udata;
4148 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4149 user_data->uuid = id;
4156 * Delete a track by the given name
4157 * NOTE: ATM this will delete the first encountered Track with the specified name
4158 * as there may be multiple tracks with the same name within the specified hash table
4160 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4163 // Fake a track with the given name
4164 udata.trk = vik_track_new ();
4165 vik_track_set_name (udata.trk, name);
4166 // Currently only the name is used in this waypoint find function
4169 // Hmmm, want key of it
4170 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4172 vik_track_free (udata.trk);
4174 if ( trkf && udata.uuid ) {
4175 // This could be a little better written...
4176 if ( vtl->tracks == ht_tracks )
4177 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4178 if ( vtl->routes == ht_tracks )
4179 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4186 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4188 vik_treeview_item_delete (vt, it );
4191 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4194 vtl->current_track = NULL;
4195 vtl->route_finder_current_track = NULL;
4196 vtl->route_finder_added_track = NULL;
4197 if (vtl->current_tp_track)
4198 trw_layer_cancel_current_tp(vtl, FALSE);
4200 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4201 g_hash_table_remove_all(vtl->routes_iters);
4202 g_hash_table_remove_all(vtl->routes);
4204 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4206 vik_layer_emit_update ( VIK_LAYER(vtl) );
4209 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4212 vtl->current_track = NULL;
4213 vtl->route_finder_current_track = NULL;
4214 vtl->route_finder_added_track = NULL;
4215 if (vtl->current_tp_track)
4216 trw_layer_cancel_current_tp(vtl, FALSE);
4218 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4219 g_hash_table_remove_all(vtl->tracks_iters);
4220 g_hash_table_remove_all(vtl->tracks);
4222 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4224 vik_layer_emit_update ( VIK_LAYER(vtl) );
4227 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4229 vtl->current_wp = NULL;
4230 vtl->current_wp_id = NULL;
4231 vtl->moving_wp = FALSE;
4233 highest_wp_number_reset(vtl);
4235 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4236 g_hash_table_remove_all(vtl->waypoints_iters);
4237 g_hash_table_remove_all(vtl->waypoints);
4239 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4241 vik_layer_emit_update ( VIK_LAYER(vtl) );
4244 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4246 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4247 // Get confirmation from the user
4248 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4249 _("Are you sure you want to delete all tracks in %s?"),
4250 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4251 vik_trw_layer_delete_all_tracks (vtl);
4254 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4256 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4257 // Get confirmation from the user
4258 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4259 _("Are you sure you want to delete all routes in %s?"),
4260 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4261 vik_trw_layer_delete_all_routes (vtl);
4264 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4266 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4267 // Get confirmation from the user
4268 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4269 _("Are you sure you want to delete all waypoints in %s?"),
4270 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4271 vik_trw_layer_delete_all_waypoints (vtl);
4274 static void trw_layer_delete_item ( gpointer pass_along[6] )
4276 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4277 gboolean was_visible = FALSE;
4278 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4280 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4281 if ( wp && wp->name ) {
4282 if ( GPOINTER_TO_INT ( pass_along[4]) )
4283 // Get confirmation from the user
4284 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4285 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4286 _("Are you sure you want to delete the waypoint \"%s\""),
4289 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4292 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4294 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4295 if ( trk && trk->name ) {
4296 if ( GPOINTER_TO_INT ( pass_along[4]) )
4297 // Get confirmation from the user
4298 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4299 _("Are you sure you want to delete the track \"%s\""),
4302 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4307 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4308 if ( trk && trk->name ) {
4309 if ( GPOINTER_TO_INT ( pass_along[4]) )
4310 // Get confirmation from the user
4311 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4312 _("Are you sure you want to delete the route \"%s\""),
4315 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4319 vik_layer_emit_update ( VIK_LAYER(vtl) );
4323 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4325 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4327 vik_waypoint_set_name ( wp, new_name );
4329 // Now update the treeview as well
4334 // Need key of it for treeview update
4335 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4337 if ( wpf && udataU.uuid ) {
4338 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4341 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4342 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4343 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, new_name );
4349 static void trw_layer_properties_item ( gpointer pass_along[7] )
4351 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4352 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4354 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4356 if ( wp && wp->name )
4358 gboolean updated = FALSE;
4359 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4361 trw_layer_waypoint_rename ( vtl, wp, new_name );
4363 if ( updated && pass_along[6] )
4364 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4366 if ( updated && VIK_LAYER(vtl)->visible )
4367 vik_layer_emit_update ( VIK_LAYER(vtl) );
4373 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4374 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4376 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4378 if ( tr && tr->name )
4380 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4383 pass_along[1], /* vlp */
4384 pass_along[5], /* vvp */
4385 pass_along[6]); /* iter */
4391 * Update the treeview of the track id - primarily to update the icon
4393 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4399 gpointer *trkf = NULL;
4400 if ( trk->is_route )
4401 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4403 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4405 if ( trkf && udata.uuid ) {
4407 GtkTreeIter *iter = NULL;
4408 if ( trk->is_route )
4409 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4411 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4414 // TODO: Make this a function
4415 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4416 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4417 ((trk->color.green & 0xff00) << 8) |
4418 (trk->color.blue & 0xff00);
4419 gdk_pixbuf_fill ( pixbuf, pixel );
4420 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4421 g_object_unref (pixbuf);
4428 Parameter 1 -> VikLayersPanel
4429 Parameter 2 -> VikLayer
4430 Parameter 3 -> VikViewport
4432 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4435 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4436 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4439 /* since vlp not set, vl & vvp should be valid instead! */
4441 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4442 vik_layer_emit_update ( VIK_LAYER(vl) );
4447 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4449 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4451 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4452 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4454 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4456 if ( track && track->trackpoints )
4457 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4460 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4462 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4464 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4465 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4467 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4469 if ( track && track->trackpoints )
4471 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4473 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4474 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4475 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4476 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4477 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4481 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4483 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4485 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4486 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4488 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4493 // Converting a track to a route can be a bit more complicated,
4494 // so give a chance to change our minds:
4495 if ( !trk->is_route &&
4496 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4497 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4499 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4500 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4505 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4508 trk_copy->is_route = !trk_copy->is_route;
4510 // ATM can't set name to self - so must create temporary copy
4511 gchar *name = g_strdup ( trk_copy->name );
4513 // Delete old one and then add new one
4514 if ( trk->is_route ) {
4515 vik_trw_layer_delete_route ( vtl, trk );
4516 vik_trw_layer_add_track ( vtl, name, trk_copy );
4519 // Extra route conversion bits...
4520 vik_track_merge_segments ( trk_copy );
4521 vik_track_to_routepoints ( trk_copy );
4523 vik_trw_layer_delete_track ( vtl, trk );
4524 vik_trw_layer_add_route ( vtl, name, trk_copy );
4528 // Update in case color of track / route changes when moving between sublayers
4529 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4533 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4535 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4537 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4538 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4540 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4545 vtl->current_track = track;
4546 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);
4548 if ( track->trackpoints )
4549 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4552 #ifdef VIK_CONFIG_GOOGLE
4554 * extend a track using route finder
4556 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4558 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4559 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4562 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4564 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4565 vtl->route_finder_coord = last_coord;
4566 vtl->route_finder_current_track = track;
4567 vtl->route_finder_started = TRUE;
4569 if ( track->trackpoints )
4570 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4575 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4577 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4578 /* Also warn if overwrite old elevation data */
4579 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4581 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4582 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4584 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4587 vik_track_apply_dem_data ( track );
4590 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4592 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4594 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4595 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4597 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4602 GList *trps = track->trackpoints;
4605 trps = g_list_last(trps);
4606 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4609 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4611 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4613 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4614 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4616 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4621 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4624 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4627 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4629 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4631 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4632 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4634 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4639 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4642 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4645 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4647 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4649 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4650 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4652 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4657 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4660 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4664 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4666 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4668 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4670 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4671 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4673 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4675 if ( trk && trk->trackpoints )
4677 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4678 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4679 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4680 if ( pass_along[1] )
4681 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4683 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4687 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4689 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4690 trw_layer_tpwin_init ( vtl );
4693 /*************************************
4694 * merge/split by time routines
4695 *************************************/
4697 /* called for each key in track hash table.
4698 * If the current track has the same time stamp type, add it to the result,
4699 * except the one pointed by "exclude".
4700 * set exclude to NULL if there is no exclude to check.
4701 * Note that the result is in reverse (for performance reasons).
4706 gboolean with_timestamps;
4708 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4710 twt_udata *user_data = udata;
4711 VikTrackpoint *p1, *p2;
4713 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4717 if (VIK_TRACK(value)->trackpoints) {
4718 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4719 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4721 if ( user_data->with_timestamps ) {
4722 if (!p1->has_timestamp || !p2->has_timestamp) {
4727 // Don't add tracks with timestamps when getting non timestamp tracks
4728 if (p1->has_timestamp || p2->has_timestamp) {
4734 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4737 /* called for each key in track hash table. if original track user_data[1] is close enough
4738 * to the passed one, add it to list in user_data[0]
4740 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4743 VikTrackpoint *p1, *p2;
4744 VikTrack *trk = VIK_TRACK(value);
4746 GList **nearby_tracks = ((gpointer *)user_data)[0];
4747 GList *tpoints = ((gpointer *)user_data)[1];
4750 * detect reasons for not merging, and return
4751 * if no reason is found not to merge, then do it.
4754 // Exclude the original track from the compiled list
4755 if (trk->trackpoints == tpoints) {
4759 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4760 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4762 if (trk->trackpoints) {
4763 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4764 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4766 if (!p1->has_timestamp || !p2->has_timestamp) {
4767 //g_print("no timestamp\n");
4771 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4772 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4773 if (! (abs(t1 - p2->timestamp) < threshold ||
4775 abs(p1->timestamp - t2) < threshold)
4782 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4785 /* comparison function used to sort tracks; a and b are hash table keys */
4786 /* Not actively used - can be restored if needed
4787 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4789 GHashTable *tracks = user_data;
4792 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4793 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4795 if (t1 < t2) return -1;
4796 if (t1 > t2) return 1;
4801 /* comparison function used to sort trackpoints */
4802 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4804 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4806 if (t1 < t2) return -1;
4807 if (t1 > t2) return 1;
4812 * comparison function which can be used to sort tracks or waypoints by name
4814 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4816 const gchar* namea = (const gchar*) a;
4817 const gchar* nameb = (const gchar*) b;
4818 if ( namea == NULL || nameb == NULL)
4821 // Same sort method as used in the vik_treeview_*_alphabetize functions
4822 return strcmp ( namea, nameb );
4826 * Attempt to merge selected track with other tracks specified by the user
4827 * Tracks to merge with must be of the same 'type' as the selected track -
4828 * either all with timestamps, or all without timestamps
4830 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4832 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4833 GList *other_tracks = NULL;
4834 GHashTable *ght_tracks;
4835 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4836 ght_tracks = vtl->routes;
4838 ght_tracks = vtl->tracks;
4840 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4845 if ( !track->trackpoints )
4849 udata.result = &other_tracks;
4850 udata.exclude = track->trackpoints;
4851 // Allow merging with 'similar' time type time tracks
4852 // i.e. either those times, or those without
4853 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4855 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4856 other_tracks = g_list_reverse(other_tracks);
4858 if ( !other_tracks ) {
4859 if ( udata.with_timestamps )
4860 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4862 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4866 // Sort alphabetically for user presentation
4867 // Convert into list of names for usage with dialog function
4868 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4869 GList *other_tracks_names = NULL;
4870 GList *iter = g_list_first ( other_tracks );
4872 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4873 iter = g_list_next ( iter );
4876 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4878 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4882 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4883 g_list_free(other_tracks);
4884 g_list_free(other_tracks_names);
4889 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4890 VikTrack *merge_track;
4891 if ( track->is_route )
4892 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4894 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4897 vik_track_steal_and_append_trackpoints ( track, merge_track );
4898 if ( track->is_route )
4899 vik_trw_layer_delete_route (vtl, merge_track);
4901 vik_trw_layer_delete_track (vtl, merge_track);
4902 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4905 for (l = merge_list; l != NULL; l = g_list_next(l))
4907 g_list_free(merge_list);
4909 vik_layer_emit_update( VIK_LAYER(vtl) );
4913 // c.f. trw_layer_sorted_track_id_by_name_list
4914 // but don't add the specified track to the list (normally current track)
4915 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4917 twt_udata *user_data = udata;
4920 if (trk->trackpoints == user_data->exclude) {
4924 // Sort named list alphabetically
4925 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4929 * Join - this allows combining 'tracks' and 'track routes'
4930 * i.e. doesn't care about whether tracks have consistent timestamps
4931 * ATM can only append one track at a time to the currently selected track
4933 static void trw_layer_append_track ( gpointer pass_along[6] )
4936 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4938 GHashTable *ght_tracks;
4939 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4940 ght_tracks = vtl->routes;
4942 ght_tracks = vtl->tracks;
4944 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4949 GList *other_tracks_names = NULL;
4951 // Sort alphabetically for user presentation
4952 // Convert into list of names for usage with dialog function
4953 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4955 udata.result = &other_tracks_names;
4956 udata.exclude = trk->trackpoints;
4958 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4960 // Note the limit to selecting one track only
4961 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4962 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4963 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4966 trk->is_route ? _("Append Route"): _("Append Track"),
4967 trk->is_route ? _("Select the route to append after the current route") :
4968 _("Select the track to append after the current track") );
4970 g_list_free(other_tracks_names);
4972 // It's a list, but shouldn't contain more than one other track!
4973 if ( append_list ) {
4975 for (l = append_list; l != NULL; l = g_list_next(l)) {
4976 // TODO: at present this uses the first track found by name,
4977 // which with potential multiple same named tracks may not be the one selected...
4978 VikTrack *append_track;
4979 if ( trk->is_route )
4980 append_track = vik_trw_layer_get_route ( vtl, l->data );
4982 append_track = vik_trw_layer_get_track ( vtl, l->data );
4984 if ( append_track ) {
4985 vik_track_steal_and_append_trackpoints ( trk, append_track );
4986 if ( trk->is_route )
4987 vik_trw_layer_delete_route (vtl, append_track);
4989 vik_trw_layer_delete_track (vtl, append_track);
4992 for (l = append_list; l != NULL; l = g_list_next(l))
4994 g_list_free(append_list);
4996 vik_layer_emit_update( VIK_LAYER(vtl) );
5001 * Very similar to trw_layer_append_track for joining
5002 * but this allows selection from the 'other' list
5003 * If a track is selected, then is shows routes and joins the selected one
5004 * If a route is selected, then is shows tracks and joins the selected one
5006 static void trw_layer_append_other ( gpointer pass_along[6] )
5009 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5011 GHashTable *ght_mykind, *ght_others;
5012 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5013 ght_mykind = vtl->routes;
5014 ght_others = vtl->tracks;
5017 ght_mykind = vtl->tracks;
5018 ght_others = vtl->routes;
5021 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5026 GList *other_tracks_names = NULL;
5028 // Sort alphabetically for user presentation
5029 // Convert into list of names for usage with dialog function
5030 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5032 udata.result = &other_tracks_names;
5033 udata.exclude = trk->trackpoints;
5035 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5037 // Note the limit to selecting one track only
5038 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5039 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5040 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5043 trk->is_route ? _("Append Track"): _("Append Route"),
5044 trk->is_route ? _("Select the track to append after the current route") :
5045 _("Select the route to append after the current track") );
5047 g_list_free(other_tracks_names);
5049 // It's a list, but shouldn't contain more than one other track!
5050 if ( append_list ) {
5052 for (l = append_list; l != NULL; l = g_list_next(l)) {
5053 // TODO: at present this uses the first track found by name,
5054 // which with potential multiple same named tracks may not be the one selected...
5056 // Get FROM THE OTHER TYPE list
5057 VikTrack *append_track;
5058 if ( trk->is_route )
5059 append_track = vik_trw_layer_get_track ( vtl, l->data );
5061 append_track = vik_trw_layer_get_route ( vtl, l->data );
5063 if ( append_track ) {
5065 if ( !append_track->is_route &&
5066 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5067 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5069 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5070 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5071 vik_track_merge_segments ( append_track );
5072 vik_track_to_routepoints ( append_track );
5079 vik_track_steal_and_append_trackpoints ( trk, append_track );
5081 // Delete copied which is FROM THE OTHER TYPE list
5082 if ( trk->is_route )
5083 vik_trw_layer_delete_track (vtl, append_track);
5085 vik_trw_layer_delete_route (vtl, append_track);
5088 for (l = append_list; l != NULL; l = g_list_next(l))
5090 g_list_free(append_list);
5091 vik_layer_emit_update( VIK_LAYER(vtl) );
5095 /* merge by segments */
5096 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5098 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5099 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5100 guint segments = vik_track_merge_segments ( trk );
5101 // NB currently no need to redraw as segments not actually shown on the display
5102 // However inform the user of what happened:
5104 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5105 g_snprintf(str, 64, tmp_str, segments);
5106 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5109 /* merge by time routine */
5110 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5112 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5116 GList *tracks_with_timestamp = NULL;
5117 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5118 if (orig_trk->trackpoints &&
5119 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5120 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5125 udata.result = &tracks_with_timestamp;
5126 udata.exclude = orig_trk->trackpoints;
5127 udata.with_timestamps = TRUE;
5128 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5129 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5131 if (!tracks_with_timestamp) {
5132 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5135 g_list_free(tracks_with_timestamp);
5137 static guint threshold_in_minutes = 1;
5138 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5139 _("Merge Threshold..."),
5140 _("Merge when time between tracks less than:"),
5141 &threshold_in_minutes)) {
5145 // keep attempting to merge all tracks until no merges within the time specified is possible
5146 gboolean attempt_merge = TRUE;
5147 GList *nearby_tracks = NULL;
5149 static gpointer params[3];
5151 while ( attempt_merge ) {
5153 // Don't try again unless tracks have changed
5154 attempt_merge = FALSE;
5156 trps = orig_trk->trackpoints;
5160 if (nearby_tracks) {
5161 g_list_free(nearby_tracks);
5162 nearby_tracks = NULL;
5165 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5166 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5168 /* g_print("Original track times: %d and %d\n", t1, t2); */
5169 params[0] = &nearby_tracks;
5170 params[1] = (gpointer)trps;
5171 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5173 /* get a list of adjacent-in-time tracks */
5174 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5177 GList *l = nearby_tracks;
5180 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5181 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5183 t1 = get_first_trackpoint(l)->timestamp;
5184 t2 = get_last_trackpoint(l)->timestamp;
5185 #undef get_first_trackpoint
5186 #undef get_last_trackpoint
5187 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5190 /* remove trackpoints from merged track, delete track */
5191 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5192 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5194 // Tracks have changed, therefore retry again against all the remaining tracks
5195 attempt_merge = TRUE;
5200 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5203 g_list_free(nearby_tracks);
5205 vik_layer_emit_update( VIK_LAYER(vtl) );
5209 * Split a track at the currently selected trackpoint
5211 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5213 if ( !vtl->current_tpl )
5216 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5217 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5219 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5220 GList *newglist = g_list_alloc ();
5221 newglist->prev = NULL;
5222 newglist->next = vtl->current_tpl->next;
5223 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5224 tr->trackpoints = newglist;
5226 vtl->current_tpl->next->prev = newglist; /* end old track here */
5227 vtl->current_tpl->next = NULL;
5229 // Bounds of the selected track changed due to the split
5230 vik_track_calculate_bounds ( vtl->current_tp_track );
5232 vtl->current_tpl = newglist; /* change tp to first of new track. */
5233 vtl->current_tp_track = tr;
5236 vik_trw_layer_add_route ( vtl, name, tr );
5238 vik_trw_layer_add_track ( vtl, name, tr );
5240 // Bounds of the new track created by the split
5241 vik_track_calculate_bounds ( tr );
5247 // Also need id of newly created track
5250 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5252 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5254 if ( trkf && udata.uuid )
5255 vtl->current_tp_id = udata.uuid;
5257 vtl->current_tp_id = NULL;
5259 vik_layer_emit_update(VIK_LAYER(vtl));
5264 /* split by time routine */
5265 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5267 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5268 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5269 GList *trps = track->trackpoints;
5271 GList *newlists = NULL;
5272 GList *newtps = NULL;
5273 static guint thr = 1;
5280 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5281 _("Split Threshold..."),
5282 _("Split when time between trackpoints exceeds:"),
5287 /* iterate through trackpoints, and copy them into new lists without touching original list */
5288 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5292 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5294 g_print("panic: ts < prev_ts: this should never happen!\n");
5297 if (ts - prev_ts > thr*60) {
5298 /* flush accumulated trackpoints into new list */
5299 newlists = g_list_append(newlists, g_list_reverse(newtps));
5303 /* accumulate trackpoint copies in newtps, in reverse order */
5304 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5306 iter = g_list_next(iter);
5309 newlists = g_list_append(newlists, g_list_reverse(newtps));
5312 /* put lists of trackpoints into tracks */
5314 // Only bother updating if the split results in new tracks
5315 if (g_list_length (newlists) > 1) {
5320 tr = vik_track_copy ( track, FALSE );
5321 tr->trackpoints = (GList *)(iter->data);
5323 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5324 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5325 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5326 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5327 vik_track_calculate_bounds ( tr );
5329 iter = g_list_next(iter);
5331 // Remove original track and then update the display
5332 vik_trw_layer_delete_track (vtl, track);
5333 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5335 g_list_free(newlists);
5339 * Split a track by the number of points as specified by the user
5341 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5343 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5345 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5346 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5348 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5353 // Check valid track
5354 GList *trps = track->trackpoints;
5358 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5359 _("Split Every Nth Point"),
5360 _("Split on every Nth point:"),
5361 250, // Default value as per typical limited track capacity of various GPS devices
5365 // Was a valid number returned?
5371 GList *newlists = NULL;
5372 GList *newtps = NULL;
5377 /* accumulate trackpoint copies in newtps, in reverse order */
5378 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5380 if (count >= points) {
5381 /* flush accumulated trackpoints into new list */
5382 newlists = g_list_append(newlists, g_list_reverse(newtps));
5386 iter = g_list_next(iter);
5389 // If there is a remaining chunk put that into the new split list
5390 // This may well be the whole track if no split points were encountered
5392 newlists = g_list_append(newlists, g_list_reverse(newtps));
5395 /* put lists of trackpoints into tracks */
5397 // Only bother updating if the split results in new tracks
5398 if (g_list_length (newlists) > 1) {
5403 tr = vik_track_copy ( track, FALSE );
5404 tr->trackpoints = (GList *)(iter->data);
5406 if ( track->is_route ) {
5407 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5408 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5411 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5412 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5414 vik_track_calculate_bounds ( tr );
5416 iter = g_list_next(iter);
5418 // Remove original track and then update the display
5419 if ( track->is_route )
5420 vik_trw_layer_delete_route (vtl, track);
5422 vik_trw_layer_delete_track (vtl, track);
5423 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5425 g_list_free(newlists);
5429 * Split a track at the currently selected trackpoint
5431 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5433 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5434 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5435 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5439 * Split a track by its segments
5440 * Routes do not have segments so don't call this for routes
5442 static void trw_layer_split_segments ( gpointer pass_along[6] )
5444 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5445 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5452 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5455 for ( i = 0; i < ntracks; i++ ) {
5457 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5458 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5463 // Remove original track
5464 vik_trw_layer_delete_track ( vtl, trk );
5465 vik_layer_emit_update ( VIK_LAYER(vtl) );
5468 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5471 /* end of split/merge routines */
5474 * Delete adjacent track points at the same position
5475 * AKA Delete Dulplicates on the Properties Window
5477 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5479 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5481 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5482 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5484 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5489 gulong removed = vik_track_remove_dup_points ( trk );
5491 // Track has been updated so update tps:
5492 trw_layer_cancel_tps_of_track ( vtl, trk );
5494 // Inform user how much was deleted as it's not obvious from the normal view
5496 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5497 g_snprintf(str, 64, tmp_str, removed);
5498 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5500 vik_layer_emit_update ( VIK_LAYER(vtl) );
5504 * Delete adjacent track points with the same timestamp
5505 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5507 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5509 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5511 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5512 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5514 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5519 gulong removed = vik_track_remove_same_time_points ( trk );
5521 // Track has been updated so update tps:
5522 trw_layer_cancel_tps_of_track ( vtl, trk );
5524 // Inform user how much was deleted as it's not obvious from the normal view
5526 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5527 g_snprintf(str, 64, tmp_str, removed);
5528 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5530 vik_layer_emit_update ( VIK_LAYER(vtl) );
5536 static void trw_layer_reverse ( gpointer pass_along[6] )
5538 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5540 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5541 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5543 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5548 vik_track_reverse ( track );
5550 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5554 * Similar to trw_layer_enum_item, but this uses a sorted method
5557 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5559 GList **list = (GList**)udata;
5560 // *list = g_list_prepend(*all, key); //unsorted method
5561 // Sort named list alphabetically
5562 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5567 * Now Waypoint specific sort
5569 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5571 GList **list = (GList**)udata;
5572 // Sort named list alphabetically
5573 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5577 * Track specific sort
5579 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5581 GList **list = (GList**)udata;
5582 // Sort named list alphabetically
5583 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5588 gboolean has_same_track_name;
5589 const gchar *same_track_name;
5590 } same_track_name_udata;
5592 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5594 const gchar* namea = (const gchar*) aa;
5595 const gchar* nameb = (const gchar*) bb;
5598 gint result = strcmp ( namea, nameb );
5600 if ( result == 0 ) {
5601 // Found two names the same
5602 same_track_name_udata *user_data = udata;
5603 user_data->has_same_track_name = TRUE;
5604 user_data->same_track_name = namea;
5607 // Leave ordering the same
5612 * Find out if any tracks have the same name in this hash table
5614 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5616 // Sort items by name, then compare if any next to each other are the same
5618 GList *track_names = NULL;
5619 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5622 if ( ! track_names )
5625 same_track_name_udata udata;
5626 udata.has_same_track_name = FALSE;
5628 // Use sort routine to traverse list comparing items
5629 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5630 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5631 // Still no tracks...
5635 return udata.has_same_track_name;
5639 * Force unqiue track names for the track table specified
5640 * Note the panel is a required parameter to enable the update of the names displayed
5641 * Specify if on tracks or else on routes
5643 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5645 // . Search list for an instance of repeated name
5646 // . get track of this name
5647 // . create new name
5648 // . rename track & update equiv. treeview iter
5649 // . repeat until all different
5651 same_track_name_udata udata;
5653 GList *track_names = NULL;
5654 udata.has_same_track_name = FALSE;
5655 udata.same_track_name = NULL;
5657 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5660 if ( ! track_names )
5663 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5665 // Still no tracks...
5666 if ( ! dummy_list1 )
5669 while ( udata.has_same_track_name ) {
5671 // Find a track with the same name
5674 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5676 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5680 g_critical("Houston, we've had a problem.");
5681 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5682 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5687 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5688 vik_track_set_name ( trk, newname );
5694 // Need want key of it for treeview update
5695 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5697 if ( trkf && udataU.uuid ) {
5701 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5703 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5706 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5707 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5708 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5713 // Start trying to find same names again...
5715 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5716 udata.has_same_track_name = FALSE;
5717 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5719 // No tracks any more - give up searching
5720 if ( ! dummy_list2 )
5721 udata.has_same_track_name = FALSE;
5725 vik_layers_panel_emit_update ( vlp );
5731 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5733 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5736 // Ensure list of track names offered is unique
5737 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5738 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5739 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5740 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5746 // Sort list alphabetically for better presentation
5747 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5750 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5754 // Get list of items to delete from the user
5755 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5758 _("Delete Selection"),
5759 _("Select tracks to delete"));
5762 // Delete requested tracks
5763 // since specificly requested, IMHO no need for extra confirmation
5764 if ( delete_list ) {
5766 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5767 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5768 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5770 g_list_free(delete_list);
5771 vik_layer_emit_update( VIK_LAYER(vtl) );
5778 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5780 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5783 // Ensure list of track names offered is unique
5784 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5785 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5786 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5787 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5793 // Sort list alphabetically for better presentation
5794 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5797 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5801 // Get list of items to delete from the user
5802 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5805 _("Delete Selection"),
5806 _("Select routes to delete") );
5809 // Delete requested routes
5810 // since specificly requested, IMHO no need for extra confirmation
5811 if ( delete_list ) {
5813 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5814 // This deletes first route it finds of that name (but uniqueness is enforced above)
5815 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5817 g_list_free(delete_list);
5818 vik_layer_emit_update( VIK_LAYER(vtl) );
5823 gboolean has_same_waypoint_name;
5824 const gchar *same_waypoint_name;
5825 } same_waypoint_name_udata;
5827 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5829 const gchar* namea = (const gchar*) aa;
5830 const gchar* nameb = (const gchar*) bb;
5833 gint result = strcmp ( namea, nameb );
5835 if ( result == 0 ) {
5836 // Found two names the same
5837 same_waypoint_name_udata *user_data = udata;
5838 user_data->has_same_waypoint_name = TRUE;
5839 user_data->same_waypoint_name = namea;
5842 // Leave ordering the same
5847 * Find out if any waypoints have the same name in this layer
5849 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5851 // Sort items by name, then compare if any next to each other are the same
5853 GList *waypoint_names = NULL;
5854 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5857 if ( ! waypoint_names )
5860 same_waypoint_name_udata udata;
5861 udata.has_same_waypoint_name = FALSE;
5863 // Use sort routine to traverse list comparing items
5864 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5865 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5866 // Still no waypoints...
5870 return udata.has_same_waypoint_name;
5874 * Force unqiue waypoint names for this layer
5875 * Note the panel is a required parameter to enable the update of the names displayed
5877 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5879 // . Search list for an instance of repeated name
5880 // . get waypoint of this name
5881 // . create new name
5882 // . rename waypoint & update equiv. treeview iter
5883 // . repeat until all different
5885 same_waypoint_name_udata udata;
5887 GList *waypoint_names = NULL;
5888 udata.has_same_waypoint_name = FALSE;
5889 udata.same_waypoint_name = NULL;
5891 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5894 if ( ! waypoint_names )
5897 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5899 // Still no waypoints...
5900 if ( ! dummy_list1 )
5903 while ( udata.has_same_waypoint_name ) {
5905 // Find a waypoint with the same name
5906 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5910 g_critical("Houston, we've had a problem.");
5911 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5912 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5917 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5919 trw_layer_waypoint_rename ( vtl, waypoint, newname );
5921 // Start trying to find same names again...
5922 waypoint_names = NULL;
5923 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5924 udata.has_same_waypoint_name = FALSE;
5925 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5927 // No waypoints any more - give up searching
5928 if ( ! dummy_list2 )
5929 udata.has_same_waypoint_name = FALSE;
5933 vik_layers_panel_emit_update ( vlp );
5939 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5941 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5944 // Ensure list of waypoint names offered is unique
5945 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5946 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5947 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5948 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5954 // Sort list alphabetically for better presentation
5955 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5957 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5961 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5963 // Get list of items to delete from the user
5964 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5967 _("Delete Selection"),
5968 _("Select waypoints to delete"));
5971 // Delete requested waypoints
5972 // since specificly requested, IMHO no need for extra confirmation
5973 if ( delete_list ) {
5975 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5976 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5977 trw_layer_delete_waypoint_by_name (vtl, l->data);
5979 g_list_free(delete_list);
5981 trw_layer_calculate_bounds_waypoints ( vtl );
5982 vik_layer_emit_update( VIK_LAYER(vtl) );
5987 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5989 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5991 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5994 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5996 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5999 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6000 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6004 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6006 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6009 if ( !strncmp(wp->comment, "http", 4) ) {
6010 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6011 } else if ( !strncmp(wp->description, "http", 4) ) {
6012 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6016 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6018 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6020 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6022 // No actual change to the name supplied
6024 if (strcmp(newname, wp->name) == 0 )
6027 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6030 // An existing waypoint has been found with the requested name
6031 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6032 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6037 // Update WP name and refresh the treeview
6038 vik_waypoint_set_name (wp, newname);
6040 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6041 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6044 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6049 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6051 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6053 // No actual change to the name supplied
6055 if (strcmp(newname, trk->name) == 0)
6058 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6061 // An existing track has been found with the requested name
6062 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6063 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6067 // Update track name and refresh GUI parts
6068 vik_track_set_name (trk, newname);
6070 // Update any subwindows that could be displaying this track which has changed name
6071 // Only one Track Edit Window
6072 if ( l->current_tp_track == trk && l->tpwin ) {
6073 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6075 // Property Dialog of the track
6076 vik_trw_layer_propwin_update ( trk );
6078 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6079 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6082 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6087 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6089 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6091 // No actual change to the name supplied
6093 if (strcmp(newname, trk->name) == 0)
6096 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6099 // An existing track has been found with the requested name
6100 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6101 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6105 // Update track name and refresh GUI parts
6106 vik_track_set_name (trk, newname);
6108 // Update any subwindows that could be displaying this track which has changed name
6109 // Only one Track Edit Window
6110 if ( l->current_tp_track == trk && l->tpwin ) {
6111 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6113 // Property Dialog of the track
6114 vik_trw_layer_propwin_update ( trk );
6116 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
6117 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
6120 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6127 static gboolean is_valid_geocache_name ( gchar *str )
6129 gint len = strlen ( str );
6130 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]));
6133 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6135 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6136 a_acquire_set_filter_track ( trk );
6139 #ifdef VIK_CONFIG_GOOGLE
6140 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6142 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6143 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6146 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6148 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6150 gchar *escaped = uri_escape ( tr->comment );
6151 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6152 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6159 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6160 /* viewpoint is now available instead */
6161 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6163 static gpointer pass_along[8];
6165 gboolean rv = FALSE;
6168 pass_along[1] = vlp;
6169 pass_along[2] = GINT_TO_POINTER (subtype);
6170 pass_along[3] = sublayer;
6171 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6172 pass_along[5] = vvp;
6173 pass_along[6] = iter;
6174 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6176 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6180 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6181 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6182 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6183 gtk_widget_show ( item );
6185 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6186 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6187 if (tr && tr->property_dialog)
6188 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6190 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6191 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6192 if (tr && tr->property_dialog)
6193 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6196 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6197 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6198 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6199 gtk_widget_show ( item );
6201 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6203 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6204 gtk_widget_show ( item );
6206 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6208 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6209 gtk_widget_show ( item );
6211 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6213 gboolean separator_created = FALSE;
6215 /* could be a right-click using the tool */
6216 if ( vlp != NULL ) {
6217 item = gtk_menu_item_new ();
6218 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6219 gtk_widget_show ( item );
6221 separator_created = TRUE;
6223 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6224 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6225 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6226 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6227 gtk_widget_show ( item );
6230 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6232 if ( wp && wp->name ) {
6233 if ( is_valid_geocache_name ( wp->name ) ) {
6235 if ( !separator_created ) {
6236 item = gtk_menu_item_new ();
6237 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6238 gtk_widget_show ( item );
6239 separator_created = TRUE;
6242 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6243 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6244 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6245 gtk_widget_show ( item );
6249 if ( wp && wp->image )
6251 if ( !separator_created ) {
6252 item = gtk_menu_item_new ();
6253 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6254 gtk_widget_show ( item );
6255 separator_created = TRUE;
6258 // Set up image paramater
6259 pass_along[5] = wp->image;
6261 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6262 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
6263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6264 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6265 gtk_widget_show ( item );
6267 #ifdef VIK_CONFIG_GEOTAG
6268 GtkWidget *geotag_submenu = gtk_menu_new ();
6269 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6270 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6271 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6272 gtk_widget_show ( item );
6273 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6275 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6276 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6277 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6278 gtk_widget_show ( item );
6280 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6281 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6282 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6283 gtk_widget_show ( item );
6289 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6290 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6291 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6292 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6294 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6295 gtk_widget_show ( item );
6302 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6303 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6305 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6306 gtk_widget_show ( item );
6307 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6308 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6309 gtk_widget_set_sensitive ( item, TRUE );
6311 gtk_widget_set_sensitive ( item, FALSE );
6314 item = gtk_menu_item_new ();
6315 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6316 gtk_widget_show ( item );
6319 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6322 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6323 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6324 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6325 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6326 gtk_widget_show ( item );
6329 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6331 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6332 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6333 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6334 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6335 gtk_widget_show ( item );
6337 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6338 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6340 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6341 gtk_widget_show ( item );
6343 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6344 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6345 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6346 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6347 gtk_widget_show ( item );
6349 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6350 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6351 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6352 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6353 gtk_widget_show ( item );
6356 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6360 if ( l->current_track && !l->current_track->is_route ) {
6361 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6363 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6364 gtk_widget_show ( item );
6366 item = gtk_menu_item_new ();
6367 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6368 gtk_widget_show ( item );
6371 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6372 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6373 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6374 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6375 gtk_widget_show ( item );
6377 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6378 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6380 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6381 gtk_widget_show ( item );
6382 // Make it available only when a new track *not* already in progress
6383 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6385 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6386 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6387 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6388 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6389 gtk_widget_show ( item );
6391 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6392 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6394 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6395 gtk_widget_show ( item );
6398 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6402 if ( l->current_track && l->current_track->is_route ) {
6403 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6404 // Reuse finish track method
6405 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6406 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6407 gtk_widget_show ( item );
6409 item = gtk_menu_item_new ();
6410 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6411 gtk_widget_show ( item );
6414 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6415 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6416 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6417 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6418 gtk_widget_show ( item );
6420 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6421 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6423 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6424 gtk_widget_show ( item );
6425 // Make it available only when a new track *not* already in progress
6426 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6428 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6429 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6430 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6431 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6432 gtk_widget_show ( item );
6434 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6435 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6436 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6437 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6438 gtk_widget_show ( item );
6441 GtkWidget *upload_submenu = gtk_menu_new ();
6443 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6445 item = gtk_menu_item_new ();
6446 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6447 gtk_widget_show ( item );
6449 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6450 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6451 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6452 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6453 if ( l->current_track ) {
6454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6455 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6456 gtk_widget_show ( item );
6459 item = gtk_menu_item_new ();
6460 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6461 gtk_widget_show ( item );
6464 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6465 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6467 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6468 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6469 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6470 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6471 gtk_widget_show ( item );
6473 GtkWidget *goto_submenu;
6474 goto_submenu = gtk_menu_new ();
6475 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6476 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6477 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6478 gtk_widget_show ( item );
6479 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6481 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6482 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6483 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6484 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6485 gtk_widget_show ( item );
6487 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6488 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6490 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6491 gtk_widget_show ( item );
6493 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6494 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6495 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6496 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6497 gtk_widget_show ( item );
6499 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6500 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6501 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6502 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6503 gtk_widget_show ( item );
6505 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6506 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6507 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6508 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6509 gtk_widget_show ( item );
6511 // Routes don't have speeds
6512 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6513 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6514 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6515 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6516 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6517 gtk_widget_show ( item );
6520 GtkWidget *combine_submenu;
6521 combine_submenu = gtk_menu_new ();
6522 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6523 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6524 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6525 gtk_widget_show ( item );
6526 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6528 // Routes don't have times or segments...
6529 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6530 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6531 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6532 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6533 gtk_widget_show ( item );
6535 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6536 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6537 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6538 gtk_widget_show ( item );
6541 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6542 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), 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 Track...") );
6549 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6550 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6551 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6552 gtk_widget_show ( item );
6554 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6555 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6557 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6558 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6559 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6560 gtk_widget_show ( item );
6562 GtkWidget *split_submenu;
6563 split_submenu = gtk_menu_new ();
6564 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6565 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6566 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6567 gtk_widget_show ( item );
6568 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6570 // Routes don't have times or segments...
6571 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6572 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6573 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6574 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6575 gtk_widget_show ( item );
6577 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6578 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6579 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6580 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6581 gtk_widget_show ( item );
6584 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6585 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6586 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6587 gtk_widget_show ( item );
6589 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6590 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6591 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6592 gtk_widget_show ( item );
6593 // Make it available only when a trackpoint is selected.
6594 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6596 GtkWidget *delete_submenu;
6597 delete_submenu = gtk_menu_new ();
6598 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6599 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6600 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6601 gtk_widget_show ( item );
6602 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6604 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6605 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6606 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6607 gtk_widget_show ( item );
6609 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6610 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6611 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6612 gtk_widget_show ( item );
6614 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6615 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6617 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6618 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6619 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6620 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6621 gtk_widget_show ( item );
6623 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6625 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6626 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6628 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6629 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
6630 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6631 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6632 gtk_widget_show ( item );
6635 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6636 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
6637 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6638 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6639 gtk_widget_show ( item );
6641 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6644 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6645 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6646 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6647 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6648 gtk_widget_show ( item );
6650 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6651 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6653 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6654 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6655 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6656 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6657 gtk_widget_show ( item );
6659 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6660 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6662 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6663 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6664 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6665 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6666 gtk_widget_show ( item );
6668 #ifdef VIK_CONFIG_GOOGLE
6669 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6670 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6671 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
6672 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6673 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6674 gtk_widget_show ( item );
6678 // ATM can't upload a single waypoint but can do waypoints to a GPS
6679 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6680 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6681 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6682 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6683 gtk_widget_show ( item );
6684 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6686 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6687 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6688 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6689 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6690 gtk_widget_show ( item );
6694 #ifdef VIK_CONFIG_GOOGLE
6695 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6697 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6698 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6700 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6701 gtk_widget_show ( item );
6705 // Some things aren't usable with routes
6706 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6707 #ifdef VIK_CONFIG_OPENSTREETMAP
6708 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6709 // Convert internal pointer into actual track for usage outside this file
6710 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6711 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6712 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6713 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6714 gtk_widget_show ( item );
6717 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6718 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6719 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6720 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6721 gtk_widget_show ( item );
6723 /* ATM This function is only available via the layers panel, due to needing a vlp */
6725 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6726 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6727 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6729 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6730 gtk_widget_show ( item );
6734 #ifdef VIK_CONFIG_GEOTAG
6735 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6736 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6737 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6738 gtk_widget_show ( item );
6742 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6743 // Only show on viewport popmenu when a trackpoint is selected
6744 if ( ! vlp && l->current_tpl ) {
6746 item = gtk_menu_item_new ();
6747 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6748 gtk_widget_show ( item );
6750 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6751 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6752 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6753 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6754 gtk_widget_show ( item );
6761 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6764 if (!vtl->current_tpl)
6766 if (!vtl->current_tpl->next)
6769 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6770 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6772 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6775 VikTrackpoint *tp_new = vik_trackpoint_new();
6776 struct LatLon ll_current, ll_next;
6777 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6778 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6780 /* main positional interpolation */
6781 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6782 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6784 /* Now other properties that can be interpolated */
6785 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6787 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6788 /* Note here the division is applied to each part, then added
6789 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6790 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6791 tp_new->has_timestamp = TRUE;
6794 if (tp_current->speed != NAN && tp_next->speed != NAN)
6795 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6797 /* TODO - improve interpolation of course, as it may not be correct.
6798 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6799 [similar applies if value is in radians] */
6800 if (tp_current->course != NAN && tp_next->course != NAN)
6801 tp_new->speed = (tp_current->course + tp_next->course)/2;
6803 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6805 /* Insert new point into the trackpoints list after the current TP */
6806 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6808 // Otherwise try routes
6809 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6813 gint index = g_list_index ( trk->trackpoints, tp_current );
6815 // NB no recalculation of bounds since it is inserted between points
6816 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6821 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6827 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6831 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6833 if ( vtl->current_tpl )
6835 vtl->current_tpl = NULL;
6836 vtl->current_tp_track = NULL;
6837 vtl->current_tp_id = NULL;
6838 vik_layer_emit_update(VIK_LAYER(vtl));
6842 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6844 g_assert ( vtl->tpwin != NULL );
6845 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6846 trw_layer_cancel_current_tp ( vtl, TRUE );
6848 if ( vtl->current_tpl == NULL )
6851 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6853 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6854 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6856 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6858 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6860 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6866 // Find available adjacent trackpoint
6867 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6869 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6870 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6872 // Delete current trackpoint
6873 vik_trackpoint_free ( vtl->current_tpl->data );
6874 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6876 // Set to current to the available adjacent trackpoint
6877 vtl->current_tpl = new_tpl;
6879 // Reset dialog with the available adjacent trackpoint
6880 if ( vtl->current_tp_track ) {
6881 vik_track_calculate_bounds ( vtl->current_tp_track );
6882 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6885 vik_layer_emit_update(VIK_LAYER(vtl));
6889 // Delete current trackpoint
6890 vik_trackpoint_free ( vtl->current_tpl->data );
6891 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6892 trw_layer_cancel_current_tp ( vtl, FALSE );
6895 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6897 if ( vtl->current_tp_track )
6898 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6899 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
6901 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6903 if ( vtl->current_tp_track )
6904 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6905 vik_layer_emit_update(VIK_LAYER(vtl));
6907 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6909 trw_layer_insert_tp_after_current_tp ( vtl );
6910 vik_layer_emit_update(VIK_LAYER(vtl));
6912 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6913 vik_layer_emit_update(VIK_LAYER(vtl));
6917 * trw_layer_dialog_shift:
6918 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
6920 * Try to reposition a dialog if it's over the specified coord
6921 * so to not obscure the item of interest
6923 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
6925 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
6927 // get parent window position & size
6928 gint win_pos_x, win_pos_y;
6929 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
6931 gint win_size_x, win_size_y;
6932 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
6934 // get own dialog size
6935 gint dia_size_x, dia_size_y;
6936 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
6938 // get own dialog position
6939 gint dia_pos_x, dia_pos_y;
6940 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
6942 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
6943 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
6945 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
6947 gint vp_xx, vp_yy; // In viewport pixels
6948 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
6950 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
6954 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
6956 // Transform Viewport pixels into absolute pixels
6957 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
6958 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
6960 // Is dialog over the point (to within an ^^ edge value)
6961 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
6962 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
6966 gint hh = vik_viewport_get_height ( vvp );
6968 // Consider the difference in viewport to the full window
6969 gint offset_y = dest_y;
6970 // Add difference between dialog and window sizes
6971 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
6973 if ( vp_yy > hh/2 ) {
6974 // Point in bottom half, move window to top half
6975 gtk_window_move ( dialog, dia_pos_x, offset_y );
6978 // Point in top half, move dialog down
6979 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
6983 // Shift left<->right
6984 gint ww = vik_viewport_get_width ( vvp );
6986 // Consider the difference in viewport to the full window
6987 gint offset_x = dest_x;
6988 // Add difference between dialog and window sizes
6989 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
6991 if ( vp_xx > ww/2 ) {
6992 // Point on right, move window to left
6993 gtk_window_move ( dialog, offset_x, dia_pos_y );
6996 // Point on left, move right
6997 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7005 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7009 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7010 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7011 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7012 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7014 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7016 if ( vtl->current_tpl ) {
7017 // get tp pixel position
7018 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7020 // Shift up<->down to try not to obscure the trackpoint.
7021 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7025 if ( vtl->current_tpl )
7026 if ( vtl->current_tp_track )
7027 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7028 /* set layer name and TP data */
7031 /***************************************************************************
7033 ***************************************************************************/
7035 /*** Utility data structures and functions ****/
7039 gint closest_x, closest_y;
7040 gboolean draw_images;
7041 gpointer *closest_wp_id;
7042 VikWaypoint *closest_wp;
7048 gint closest_x, closest_y;
7049 gpointer closest_track_id;
7050 VikTrackpoint *closest_tp;
7056 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7062 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7064 // If waypoint has an image then use the image size to select
7065 if ( params->draw_images && wp->image ) {
7066 gint slackx, slacky;
7067 slackx = wp->image_width / 2;
7068 slacky = wp->image_height / 2;
7070 if ( x <= params->x + slackx && x >= params->x - slackx
7071 && y <= params->y + slacky && y >= params->y - slacky ) {
7072 params->closest_wp_id = id;
7073 params->closest_wp = wp;
7074 params->closest_x = x;
7075 params->closest_y = y;
7078 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7079 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7080 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7082 params->closest_wp_id = id;
7083 params->closest_wp = wp;
7084 params->closest_x = x;
7085 params->closest_y = y;
7089 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7091 GList *tpl = t->trackpoints;
7097 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7103 tp = VIK_TRACKPOINT(tpl->data);
7105 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7107 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7108 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7109 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7111 params->closest_track_id = id;
7112 params->closest_tp = tp;
7113 params->closest_tpl = tpl;
7114 params->closest_x = x;
7115 params->closest_y = y;
7121 // ATM: Leave this as 'Track' only.
7122 // Not overly bothered about having a snap to route trackpoint capability
7123 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7125 TPSearchParams params;
7129 params.closest_track_id = NULL;
7130 params.closest_tp = NULL;
7131 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7132 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7133 return params.closest_tp;
7136 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7138 WPSearchParams params;
7142 params.draw_images = vtl->drawimages;
7143 params.closest_wp = NULL;
7144 params.closest_wp_id = NULL;
7145 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7146 return params.closest_wp;
7150 // Some forward declarations
7151 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7152 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7153 static void marker_end_move ( tool_ed_t *t );
7156 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7160 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7162 // Here always allow snapping back to the original location
7163 // this is useful when one decides not to move the thing afterall
7164 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7167 if ( event->state & GDK_CONTROL_MASK )
7169 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7171 new_coord = tp->coord;
7175 if ( event->state & GDK_SHIFT_MASK )
7177 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7179 new_coord = wp->coord;
7183 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7185 marker_moveto ( t, x, y );
7192 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7194 if ( t->holding && event->button == 1 )
7197 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7200 if ( event->state & GDK_CONTROL_MASK )
7202 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7204 new_coord = tp->coord;
7208 if ( event->state & GDK_SHIFT_MASK )
7210 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7212 new_coord = wp->coord;
7215 marker_end_move ( t );
7217 // Determine if working on a waypoint or a trackpoint
7218 if ( t->is_waypoint ) {
7219 vtl->current_wp->coord = new_coord;
7220 trw_layer_calculate_bounds_waypoints ( vtl );
7223 if ( vtl->current_tpl ) {
7224 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7226 if ( vtl->current_tp_track )
7227 vik_track_calculate_bounds ( vtl->current_tp_track );
7230 if ( vtl->current_tp_track )
7231 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7236 vtl->current_wp = NULL;
7237 vtl->current_wp_id = NULL;
7238 trw_layer_cancel_current_tp ( vtl, FALSE );
7240 vik_layer_emit_update ( VIK_LAYER(vtl) );
7247 Returns true if a waypoint or track is found near the requested event position for this particular layer
7248 The item found is automatically selected
7249 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7251 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7253 if ( event->button != 1 )
7256 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7259 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7263 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7265 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7267 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7268 WPSearchParams wp_params;
7269 wp_params.vvp = vvp;
7270 wp_params.x = event->x;
7271 wp_params.y = event->y;
7272 wp_params.draw_images = vtl->drawimages;
7273 wp_params.closest_wp_id = NULL;
7274 wp_params.closest_wp = NULL;
7276 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7278 if ( wp_params.closest_wp ) {
7281 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7283 // Too easy to move it so must be holding shift to start immediately moving it
7284 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7285 if ( event->state & GDK_SHIFT_MASK ||
7286 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7287 // Put into 'move buffer'
7288 // NB vvp & vw already set in tet
7289 tet->vtl = (gpointer)vtl;
7290 tet->is_waypoint = TRUE;
7292 marker_begin_move (tet, event->x, event->y);
7295 vtl->current_wp = wp_params.closest_wp;
7296 vtl->current_wp_id = wp_params.closest_wp_id;
7298 vik_layer_emit_update ( VIK_LAYER(vtl) );
7304 // Used for both track and route lists
7305 TPSearchParams tp_params;
7306 tp_params.vvp = vvp;
7307 tp_params.x = event->x;
7308 tp_params.y = event->y;
7309 tp_params.closest_track_id = NULL;
7310 tp_params.closest_tp = NULL;
7311 tp_params.bbox = bbox;
7313 if (vtl->tracks_visible) {
7314 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7316 if ( tp_params.closest_tp ) {
7318 // Always select + highlight the track
7319 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7321 tet->is_waypoint = FALSE;
7323 // Select the Trackpoint
7324 // Can move it immediately when control held or it's the previously selected tp
7325 if ( event->state & GDK_CONTROL_MASK ||
7326 vtl->current_tpl == tp_params.closest_tpl ) {
7327 // Put into 'move buffer'
7328 // NB vvp & vw already set in tet
7329 tet->vtl = (gpointer)vtl;
7330 marker_begin_move (tet, event->x, event->y);
7333 vtl->current_tpl = tp_params.closest_tpl;
7334 vtl->current_tp_id = tp_params.closest_track_id;
7335 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7337 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7340 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7342 vik_layer_emit_update ( VIK_LAYER(vtl) );
7347 // Try again for routes
7348 if (vtl->routes_visible) {
7349 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7351 if ( tp_params.closest_tp ) {
7353 // Always select + highlight the track
7354 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7356 tet->is_waypoint = FALSE;
7358 // Select the Trackpoint
7359 // Can move it immediately when control held or it's the previously selected tp
7360 if ( event->state & GDK_CONTROL_MASK ||
7361 vtl->current_tpl == tp_params.closest_tpl ) {
7362 // Put into 'move buffer'
7363 // NB vvp & vw already set in tet
7364 tet->vtl = (gpointer)vtl;
7365 marker_begin_move (tet, event->x, event->y);
7368 vtl->current_tpl = tp_params.closest_tpl;
7369 vtl->current_tp_id = tp_params.closest_track_id;
7370 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7372 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7375 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7377 vik_layer_emit_update ( VIK_LAYER(vtl) );
7382 /* these aren't the droids you're looking for */
7383 vtl->current_wp = NULL;
7384 vtl->current_wp_id = NULL;
7385 trw_layer_cancel_current_tp ( vtl, FALSE );
7388 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7393 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7395 if ( event->button != 3 )
7398 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7401 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7404 /* Post menu for the currently selected item */
7406 /* See if a track is selected */
7407 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7408 if ( track && track->visible ) {
7410 if ( track->name ) {
7412 if ( vtl->track_right_click_menu )
7413 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
7415 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7422 if ( track->is_route )
7423 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7425 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7427 if ( trkf && udataU.uuid ) {
7430 if ( track->is_route )
7431 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7433 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7435 trw_layer_sublayer_add_menu_items ( vtl,
7436 vtl->track_right_click_menu,
7438 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7444 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7450 /* See if a waypoint is selected */
7451 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7452 if ( waypoint && waypoint->visible ) {
7453 if ( waypoint->name ) {
7455 if ( vtl->wp_right_click_menu )
7456 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7458 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7461 udata.wp = waypoint;
7464 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7466 if ( wpf && udata.uuid ) {
7467 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7469 trw_layer_sublayer_add_menu_items ( vtl,
7470 vtl->wp_right_click_menu,
7472 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7477 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7486 /* background drawing hook, to be passed the viewport */
7487 static gboolean tool_sync_done = TRUE;
7489 static gboolean tool_sync(gpointer data)
7491 VikViewport *vvp = data;
7492 gdk_threads_enter();
7493 vik_viewport_sync(vvp);
7494 tool_sync_done = TRUE;
7495 gdk_threads_leave();
7499 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7502 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7503 gdk_gc_set_function ( t->gc, GDK_INVERT );
7504 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7505 vik_viewport_sync(t->vvp);
7510 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7512 VikViewport *vvp = t->vvp;
7513 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7514 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7518 if (tool_sync_done) {
7519 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7520 tool_sync_done = FALSE;
7524 static void marker_end_move ( tool_ed_t *t )
7526 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7527 g_object_unref ( t->gc );
7531 /*** Edit waypoint ****/
7533 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7535 tool_ed_t *t = g_new(tool_ed_t, 1);
7541 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7543 WPSearchParams params;
7544 tool_ed_t *t = data;
7545 VikViewport *vvp = t->vvp;
7547 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7554 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7557 if ( vtl->current_wp && vtl->current_wp->visible )
7559 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7561 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7563 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7564 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7566 if ( event->button == 3 )
7567 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7569 marker_begin_move(t, event->x, event->y);
7576 params.x = event->x;
7577 params.y = event->y;
7578 params.draw_images = vtl->drawimages;
7579 params.closest_wp_id = NULL;
7580 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7581 params.closest_wp = NULL;
7582 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7583 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7585 // how do we get here?
7586 marker_begin_move(t, event->x, event->y);
7587 g_critical("shouldn't be here");
7590 else if ( params.closest_wp )
7592 if ( event->button == 3 )
7593 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7595 vtl->waypoint_rightclick = FALSE;
7597 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7599 vtl->current_wp = params.closest_wp;
7600 vtl->current_wp_id = params.closest_wp_id;
7602 /* could make it so don't update if old WP is off screen and new is null but oh well */
7603 vik_layer_emit_update ( VIK_LAYER(vtl) );
7607 vtl->current_wp = NULL;
7608 vtl->current_wp_id = NULL;
7609 vtl->waypoint_rightclick = FALSE;
7610 vik_layer_emit_update ( VIK_LAYER(vtl) );
7614 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7616 tool_ed_t *t = data;
7617 VikViewport *vvp = t->vvp;
7619 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7624 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7627 if ( event->state & GDK_CONTROL_MASK )
7629 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7631 new_coord = tp->coord;
7635 if ( event->state & GDK_SHIFT_MASK )
7637 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7638 if ( wp && wp != vtl->current_wp )
7639 new_coord = wp->coord;
7644 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7646 marker_moveto ( t, x, y );
7653 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7655 tool_ed_t *t = data;
7656 VikViewport *vvp = t->vvp;
7658 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7661 if ( t->holding && event->button == 1 )
7664 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7667 if ( event->state & GDK_CONTROL_MASK )
7669 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7671 new_coord = tp->coord;
7675 if ( event->state & GDK_SHIFT_MASK )
7677 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7678 if ( wp && wp != vtl->current_wp )
7679 new_coord = wp->coord;
7682 marker_end_move ( t );
7684 vtl->current_wp->coord = new_coord;
7686 trw_layer_calculate_bounds_waypoints ( vtl );
7687 vik_layer_emit_update ( VIK_LAYER(vtl) );
7690 /* PUT IN RIGHT PLACE!!! */
7691 if ( event->button == 3 && vtl->waypoint_rightclick )
7693 if ( vtl->wp_right_click_menu )
7694 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7695 if ( vtl->current_wp ) {
7696 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7697 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 );
7698 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7700 vtl->waypoint_rightclick = FALSE;
7705 /*** New track ****/
7707 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7714 GdkDrawable *drawable;
7720 * Draw specified pixmap
7722 static gboolean draw_sync ( gpointer data )
7724 draw_sync_t *ds = (draw_sync_t*) data;
7725 // Sometimes don't want to draw
7726 // normally because another update has taken precedent such as panning the display
7727 // which means this pixmap is no longer valid
7728 if ( ds->vtl->draw_sync_do ) {
7729 gdk_threads_enter();
7730 gdk_draw_drawable (ds->drawable,
7733 0, 0, 0, 0, -1, -1);
7734 ds->vtl->draw_sync_done = TRUE;
7735 gdk_threads_leave();
7740 static gchar* distance_string (gdouble distance)
7744 /* draw label with distance */
7745 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7746 switch (dist_units) {
7747 case VIK_UNITS_DISTANCE_MILES:
7748 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7749 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7750 } else if (distance < 1609.4) {
7751 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7753 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7757 // VIK_UNITS_DISTANCE_KILOMETRES
7758 if (distance >= 1000 && distance < 100000) {
7759 g_sprintf(str, "%3.2f km", distance/1000.0);
7760 } else if (distance < 1000) {
7761 g_sprintf(str, "%d m", (int)distance);
7763 g_sprintf(str, "%d km", (int)distance/1000);
7767 return g_strdup (str);
7771 * Actually set the message in statusbar
7773 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
7775 // Only show elevation data when track has some elevation properties
7776 gchar str_gain_loss[64];
7777 str_gain_loss[0] = '\0';
7778 gchar str_last_step[64];
7779 str_last_step[0] = '\0';
7780 gchar *str_total = distance_string (distance);
7782 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7783 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7784 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7786 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7789 if ( last_step > 0 ) {
7790 gchar *tmp = distance_string (last_step);
7791 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
7795 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
7797 // Write with full gain/loss information
7798 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
7799 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
7801 g_free ( str_total );
7805 * Figure out what information should be set in the statusbar and then write it
7807 static void update_statusbar ( VikTrwLayer *vtl )
7809 // Get elevation data
7810 gdouble elev_gain, elev_loss;
7811 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7813 /* Find out actual distance of current track */
7814 gdouble distance = vik_track_get_length (vtl->current_track);
7816 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
7820 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7822 /* if we haven't sync'ed yet, we don't have time to do more. */
7823 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7824 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7825 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7827 static GdkPixmap *pixmap = NULL;
7829 // Need to check in case window has been resized
7830 w1 = vik_viewport_get_width(vvp);
7831 h1 = vik_viewport_get_height(vvp);
7833 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
7835 gdk_drawable_get_size (pixmap, &w2, &h2);
7836 if (w1 != w2 || h1 != h2) {
7837 g_object_unref ( G_OBJECT ( pixmap ) );
7838 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
7841 // Reset to background
7842 gdk_draw_drawable (pixmap,
7843 vtl->current_track_newpoint_gc,
7844 vik_viewport_get_pixmap(vvp),
7845 0, 0, 0, 0, -1, -1);
7847 draw_sync_t *passalong;
7850 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7852 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7853 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7854 // thus when we come to reset to the background it would include what we have already drawn!!
7855 gdk_draw_line ( pixmap,
7856 vtl->current_track_newpoint_gc,
7857 x1, y1, event->x, event->y );
7858 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7860 /* Find out actual distance of current track */
7861 gdouble distance = vik_track_get_length (vtl->current_track);
7863 // Now add distance to where the pointer is //
7866 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7867 vik_coord_to_latlon ( &coord, &ll );
7868 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
7869 distance = distance + last_step;
7871 // Get elevation data
7872 gdouble elev_gain, elev_loss;
7873 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7875 // Adjust elevation data (if available) for the current pointer position
7877 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7878 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7879 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7880 // Adjust elevation of last track point
7881 if ( elev_new > last_tpt->altitude )
7883 elev_gain += elev_new - last_tpt->altitude;
7886 elev_loss += last_tpt->altitude - elev_new;
7891 // Display of the distance 'tooltip' during track creation is controlled by a preference
7893 if ( a_vik_get_create_track_tooltip() ) {
7895 gchar *str = distance_string (distance);
7897 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7898 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
7899 pango_layout_set_text (pl, str, -1);
7901 pango_layout_get_pixel_size ( pl, &wd, &hd );
7904 // offset from cursor a bit depending on font size
7908 // Create a background block to make the text easier to read over the background map
7909 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7910 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7911 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7913 g_object_unref ( G_OBJECT ( pl ) );
7914 g_object_unref ( G_OBJECT ( background_block_gc ) );
7918 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7919 passalong->vtl = vtl;
7920 passalong->pixmap = pixmap;
7921 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
7922 passalong->gc = vtl->current_track_newpoint_gc;
7926 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
7928 // Update statusbar with full gain/loss information
7929 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7931 // draw pixmap when we have time to
7932 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7933 vtl->draw_sync_done = FALSE;
7934 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7936 return VIK_LAYER_TOOL_ACK;
7939 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7941 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7942 vtl->current_track = NULL;
7943 vik_layer_emit_update ( VIK_LAYER(vtl) );
7945 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7947 if ( vtl->current_track->trackpoints )
7949 GList *last = g_list_last(vtl->current_track->trackpoints);
7950 g_free ( last->data );
7951 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7954 update_statusbar ( vtl );
7956 vik_layer_emit_update ( VIK_LAYER(vtl) );
7963 * Common function to handle trackpoint button requests on either a route or a track
7964 * . enables adding a point via normal click
7965 * . enables removal of last point via right click
7966 * . finishing of the track or route via double clicking
7968 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7972 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7975 if ( event->button == 2 ) {
7976 // As the display is panning, the new track pixmap is now invalid so don't draw it
7977 // otherwise this drawing done results in flickering back to an old image
7978 vtl->draw_sync_do = FALSE;
7982 if ( event->button == 3 )
7984 if ( !vtl->current_track )
7987 if ( vtl->current_track->trackpoints )
7989 GList *last = g_list_last(vtl->current_track->trackpoints);
7990 g_free ( last->data );
7991 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7993 vik_track_calculate_bounds ( vtl->current_track );
7994 update_statusbar ( vtl );
7996 vik_layer_emit_update ( VIK_LAYER(vtl) );
8000 if ( event->type == GDK_2BUTTON_PRESS )
8002 /* subtract last (duplicate from double click) tp then end */
8003 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8005 GList *last = g_list_last(vtl->current_track->trackpoints);
8006 g_free ( last->data );
8007 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8008 /* undo last, then end */
8009 vtl->current_track = NULL;
8011 vik_layer_emit_update ( VIK_LAYER(vtl) );
8015 tp = vik_trackpoint_new();
8016 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8018 /* snap to other TP */
8019 if ( event->state & GDK_CONTROL_MASK )
8021 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8023 tp->coord = other_tp->coord;
8026 tp->newsegment = FALSE;
8027 tp->has_timestamp = FALSE;
8030 if ( vtl->current_track ) {
8031 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
8032 /* Auto attempt to get elevation from DEM data (if it's available) */
8033 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8034 vik_track_calculate_bounds ( vtl->current_track );
8037 vtl->ct_x1 = vtl->ct_x2;
8038 vtl->ct_y1 = vtl->ct_y2;
8039 vtl->ct_x2 = event->x;
8040 vtl->ct_y2 = event->y;
8042 vik_layer_emit_update ( VIK_LAYER(vtl) );
8046 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8048 // ----------------------------------------------------- if current is a route - switch to new track
8049 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8051 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8052 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8054 new_track_create_common ( vtl, name );
8059 return tool_new_track_or_route_click ( vtl, event, vvp );
8062 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8064 if ( event->button == 2 ) {
8065 // Pan moving ended - enable potential point drawing again
8066 vtl->draw_sync_do = TRUE;
8067 vtl->draw_sync_done = TRUE;
8071 /*** New route ****/
8073 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8078 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8080 // -------------------------- if current is a track - switch to new route
8081 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8083 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8084 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
8085 new_route_create_common ( vtl, name );
8089 return tool_new_track_or_route_click ( vtl, event, vvp );
8092 /*** New waypoint ****/
8094 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8099 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8102 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8104 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8105 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8106 trw_layer_calculate_bounds_waypoints ( vtl );
8107 vik_layer_emit_update ( VIK_LAYER(vtl) );
8113 /*** Edit trackpoint ****/
8115 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8117 tool_ed_t *t = g_new(tool_ed_t, 1);
8123 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8125 tool_ed_t *t = data;
8126 VikViewport *vvp = t->vvp;
8127 TPSearchParams params;
8128 /* OUTDATED DOCUMENTATION:
8129 find 5 pixel range on each side. then put these UTM, and a pointer
8130 to the winning track name (and maybe the winning track itself), and a
8131 pointer to the winning trackpoint, inside an array or struct. pass
8132 this along, do a foreach on the tracks which will do a foreach on the
8135 params.x = event->x;
8136 params.y = event->y;
8137 params.closest_track_id = NULL;
8138 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8139 params.closest_tp = NULL;
8141 if ( event->button != 1 )
8144 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8147 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8150 if ( vtl->current_tpl )
8152 /* 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.) */
8153 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8154 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8159 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8161 if ( current_tr->visible &&
8162 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8163 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8164 marker_begin_move ( t, event->x, event->y );
8170 if ( vtl->tracks_visible )
8171 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8173 if ( params.closest_tp )
8175 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8176 vtl->current_tpl = params.closest_tpl;
8177 vtl->current_tp_id = params.closest_track_id;
8178 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8179 trw_layer_tpwin_init ( vtl );
8180 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8181 vik_layer_emit_update ( VIK_LAYER(vtl) );
8185 if ( vtl->routes_visible )
8186 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8188 if ( params.closest_tp )
8190 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8191 vtl->current_tpl = params.closest_tpl;
8192 vtl->current_tp_id = params.closest_track_id;
8193 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8194 trw_layer_tpwin_init ( vtl );
8195 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8196 vik_layer_emit_update ( VIK_LAYER(vtl) );
8200 /* these aren't the droids you're looking for */
8204 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8206 tool_ed_t *t = data;
8207 VikViewport *vvp = t->vvp;
8209 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8215 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8218 if ( event->state & GDK_CONTROL_MASK )
8220 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8221 if ( tp && tp != vtl->current_tpl->data )
8222 new_coord = tp->coord;
8224 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8227 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8228 marker_moveto ( t, x, y );
8236 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8238 tool_ed_t *t = data;
8239 VikViewport *vvp = t->vvp;
8241 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8243 if ( event->button != 1)
8248 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8251 if ( event->state & GDK_CONTROL_MASK )
8253 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8254 if ( tp && tp != vtl->current_tpl->data )
8255 new_coord = tp->coord;
8258 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8259 if ( vtl->current_tp_track )
8260 vik_track_calculate_bounds ( vtl->current_tp_track );
8262 marker_end_move ( t );
8264 /* diff dist is diff from orig */
8266 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8268 vik_layer_emit_update ( VIK_LAYER(vtl) );
8275 #ifdef VIK_CONFIG_GOOGLE
8276 /*** Route Finder ***/
8277 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8282 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8285 if ( !vtl ) return FALSE;
8286 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8287 if ( event->button == 3 && vtl->route_finder_current_track ) {
8289 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8291 vtl->route_finder_coord = *new_end;
8293 vik_layer_emit_update ( VIK_LAYER(vtl) );
8294 /* remove last ' to:...' */
8295 if ( vtl->route_finder_current_track->comment ) {
8296 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8297 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8298 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8299 last_to - vtl->route_finder_current_track->comment - 1);
8300 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8305 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8306 struct LatLon start, end;
8307 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
8308 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
8311 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8312 vik_coord_to_latlon ( &(tmp), &end );
8313 vtl->route_finder_coord = tmp; /* for continuations */
8315 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8316 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8317 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8319 vtl->route_finder_check_added_track = TRUE;
8320 vtl->route_finder_started = FALSE;
8323 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
8324 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
8325 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
8326 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
8327 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
8328 // NB normally this returns a GPX Route - so subsequent usage of it must lookup via the routes hash
8329 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL, NULL );
8332 /* see if anything was done -- a track was added or appended to */
8333 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8334 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 ) );
8335 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8336 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8337 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8338 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8341 if ( vtl->route_finder_added_track )
8342 vik_track_calculate_bounds ( vtl->route_finder_added_track );
8344 vtl->route_finder_added_track = NULL;
8345 vtl->route_finder_check_added_track = FALSE;
8346 vtl->route_finder_append = FALSE;
8348 vik_layer_emit_update ( VIK_LAYER(vtl) );
8350 vtl->route_finder_started = TRUE;
8351 vtl->route_finder_coord = tmp;
8352 vtl->route_finder_current_track = NULL;
8358 /*** Show picture ****/
8360 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8365 /* Params are: vvp, event, last match found or NULL */
8366 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8368 if ( wp->image && wp->visible )
8370 gint x, y, slackx, slacky;
8371 GdkEventButton *event = (GdkEventButton *) params[1];
8373 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8374 slackx = wp->image_width / 2;
8375 slacky = wp->image_height / 2;
8376 if ( x <= event->x + slackx && x >= event->x - slackx
8377 && y <= event->y + slacky && y >= event->y - slacky )
8379 params[2] = wp->image; /* we've found a match. however continue searching
8380 * since we want to find the last match -- that
8381 * is, the match that was drawn last. */
8386 static void trw_layer_show_picture ( gpointer pass_along[6] )
8388 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8390 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8393 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8394 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8395 g_free ( quoted_file );
8396 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8398 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() );
8399 g_error_free ( err );
8402 #endif /* WINDOWS */
8405 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8407 gpointer params[3] = { vvp, event, NULL };
8408 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8410 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8413 static gpointer pass_along[6];
8414 pass_along[0] = vtl;
8415 pass_along[5] = params[2];
8416 trw_layer_show_picture ( pass_along );
8417 return TRUE; /* found a match */
8420 return FALSE; /* go through other layers, searching for a match */
8423 /***************************************************************************
8425 ***************************************************************************/
8428 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8430 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8431 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8434 /* Structure for thumbnail creating data used in the background thread */
8436 VikTrwLayer *vtl; // Layer needed for redrawing
8437 GSList *pics; // Image list
8438 } thumbnail_create_thread_data;
8440 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8442 guint total = g_slist_length(tctd->pics), done = 0;
8443 while ( tctd->pics )
8445 a_thumbnails_create ( (gchar *) tctd->pics->data );
8446 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8448 return -1; /* Abort thread */
8450 tctd->pics = tctd->pics->next;
8453 // Redraw to show the thumbnails as they are now created
8454 if ( IS_VIK_LAYER(tctd->vtl) )
8455 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8460 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8462 while ( tctd->pics )
8464 g_free ( tctd->pics->data );
8465 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8470 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8472 if ( ! vtl->has_verified_thumbnails )
8474 GSList *pics = NULL;
8475 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8478 gint len = g_slist_length ( pics );
8479 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8480 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8483 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8485 (vik_thr_func) create_thumbnails_thread,
8487 (vik_thr_free_func) thumbnail_create_thread_free,
8495 static const gchar* my_track_colors ( gint ii )
8497 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8509 // Fast and reliable way of returning a colour
8510 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8513 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8515 GHashTableIter iter;
8516 gpointer key, value;
8520 g_hash_table_iter_init ( &iter, vtl->tracks );
8522 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8524 // Tracks get a random spread of colours if not already assigned
8525 if ( ! VIK_TRACK(value)->has_color ) {
8526 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8527 VIK_TRACK(value)->color = vtl->track_color;
8529 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8531 VIK_TRACK(value)->has_color = TRUE;
8534 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8537 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8543 g_hash_table_iter_init ( &iter, vtl->routes );
8545 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8547 // Routes get an intermix of reds
8548 if ( ! VIK_TRACK(value)->has_color ) {
8550 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8552 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8553 VIK_TRACK(value)->has_color = TRUE;
8556 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8563 * (Re)Calculate the bounds of the waypoints in this layer,
8564 * This should be called whenever waypoints are changed
8566 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
8568 struct LatLon topleft = { 0.0, 0.0 };
8569 struct LatLon bottomright = { 0.0, 0.0 };
8572 GHashTableIter iter;
8573 gpointer key, value;
8575 g_hash_table_iter_init ( &iter, vtl->waypoints );
8577 // Set bounds to first point
8578 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
8579 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
8580 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
8583 // Ensure there is another point...
8584 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
8586 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8588 // See if this point increases the bounds.
8589 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
8591 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
8592 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
8593 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
8594 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
8598 vtl->waypoints_bbox.north = topleft.lat;
8599 vtl->waypoints_bbox.east = bottomright.lon;
8600 vtl->waypoints_bbox.south = bottomright.lat;
8601 vtl->waypoints_bbox.west = topleft.lon;
8604 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
8606 vik_track_calculate_bounds ( trk );
8609 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
8611 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8612 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
8615 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
8617 trw_layer_verify_thumbnails ( vtl, vvp );
8618 trw_layer_track_alloc_colors ( vtl );
8620 trw_layer_calculate_bounds_waypoints ( vtl );
8621 trw_layer_calculate_bounds_tracks ( vtl );
8624 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8626 return vtl->coord_mode;
8630 * Uniquify the whole layer
8631 * Also requires the layers panel as the names shown there need updating too
8632 * Returns whether the operation was successful or not
8634 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8637 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8638 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8639 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8645 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8647 vik_coord_convert ( &(wp->coord), *dest_mode );
8650 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
8652 vik_track_convert ( tr, *dest_mode );
8655 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8657 if ( vtl->coord_mode != dest_mode )
8659 vtl->coord_mode = dest_mode;
8660 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8661 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8662 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
8666 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8668 vtl->menu_selection = selection;
8671 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8673 return (vtl->menu_selection);
8676 /* ----------- Downloading maps along tracks --------------- */
8678 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8680 /* TODO: calculating based on current size of viewport */
8681 const gdouble w_at_zoom_0_125 = 0.0013;
8682 const gdouble h_at_zoom_0_125 = 0.0011;
8683 gdouble zoom_factor = zoom_level/0.125;
8685 wh->lat = h_at_zoom_0_125 * zoom_factor;
8686 wh->lon = w_at_zoom_0_125 * zoom_factor;
8688 return 0; /* all OK */
8691 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8693 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8694 (dist->lat >= ABS(to->north_south - from->north_south)))
8697 VikCoord *coord = g_malloc(sizeof(VikCoord));
8698 coord->mode = VIK_COORD_LATLON;
8700 if (ABS(gradient) < 1) {
8701 if (from->east_west > to->east_west)
8702 coord->east_west = from->east_west - dist->lon;
8704 coord->east_west = from->east_west + dist->lon;
8705 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8707 if (from->north_south > to->north_south)
8708 coord->north_south = from->north_south - dist->lat;
8710 coord->north_south = from->north_south + dist->lat;
8711 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8717 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8719 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8720 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8722 VikCoord *next = from;
8724 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8726 list = g_list_prepend(list, next);
8732 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8734 typedef struct _Rect {
8739 #define GLRECT(iter) ((Rect *)((iter)->data))
8742 GList *rects_to_download = NULL;
8745 if (get_download_area_width(vvp, zoom_level, &wh))
8748 GList *iter = tr->trackpoints;
8752 gboolean new_map = TRUE;
8753 VikCoord *cur_coord, tl, br;
8756 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8758 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8759 rect = g_malloc(sizeof(Rect));
8762 rect->center = *cur_coord;
8763 rects_to_download = g_list_prepend(rects_to_download, rect);
8768 gboolean found = FALSE;
8769 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8770 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8781 GList *fillins = NULL;
8782 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8783 /* seems that ATM the function get_next_coord works only for LATLON */
8784 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8785 /* fill-ins for far apart points */
8786 GList *cur_rect, *next_rect;
8787 for (cur_rect = rects_to_download;
8788 (next_rect = cur_rect->next) != NULL;
8789 cur_rect = cur_rect->next) {
8790 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8791 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8792 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8796 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8799 GList *iter = fillins;
8801 cur_coord = (VikCoord *)(iter->data);
8802 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8803 rect = g_malloc(sizeof(Rect));
8806 rect->center = *cur_coord;
8807 rects_to_download = g_list_prepend(rects_to_download, rect);
8812 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8813 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8817 for (iter = fillins; iter; iter = iter->next)
8819 g_list_free(fillins);
8821 if (rects_to_download) {
8822 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8823 g_free(rect_iter->data);
8824 g_list_free(rects_to_download);
8828 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8831 gint selected_map, default_map;
8832 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8833 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8834 gint selected_zoom, default_zoom;
8838 VikTrwLayer *vtl = pass_along[0];
8839 VikLayersPanel *vlp = pass_along[1];
8841 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8842 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8844 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8848 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8850 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8851 int num_maps = g_list_length(vmls);
8854 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8858 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8859 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8861 gchar **np = map_names;
8862 VikMapsLayer **lp = map_layers;
8863 for (i = 0; i < num_maps; i++) {
8864 gboolean dup = FALSE;
8865 vml = (VikMapsLayer *)(vmls->data);
8866 for (j = 0; j < i; j++) { /* no duplicate allowed */
8867 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8874 *np++ = vik_maps_layer_get_map_label(vml);
8880 num_maps = lp - map_layers;
8882 for (default_map = 0; default_map < num_maps; default_map++) {
8883 /* TODO: check for parent layer's visibility */
8884 if (VIK_LAYER(map_layers[default_map])->visible)
8887 default_map = (default_map == num_maps) ? 0 : default_map;
8889 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8890 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8891 if (cur_zoom == zoom_vals[default_zoom])
8894 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8896 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8899 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8902 for (i = 0; i < num_maps; i++)
8903 g_free(map_names[i]);
8911 /**** lowest waypoint number calculation ***/
8912 static gint highest_wp_number_name_to_number(const gchar *name) {
8913 if ( strlen(name) == 3 ) {
8915 if ( n < 100 && name[0] != '0' )
8917 if ( n < 10 && name[0] != '0' )
8925 static void highest_wp_number_reset(VikTrwLayer *vtl)
8927 vtl->highest_wp_number = -1;
8930 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8932 /* if is bigger that top, add it */
8933 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8934 if ( new_wp_num > vtl->highest_wp_number )
8935 vtl->highest_wp_number = new_wp_num;
8938 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8940 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8941 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8942 if ( vtl->highest_wp_number == old_wp_num ) {
8944 vtl->highest_wp_number--;
8946 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8947 /* search down until we find something that *does* exist */
8949 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8950 vtl->highest_wp_number--;
8951 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8956 /* get lowest unused number */
8957 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8960 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8962 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8963 return g_strdup(buf);