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 -- 5000+ 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 /* Relax some dependencies */
77 #if ! GLIB_CHECK_VERSION(2,12,0)
78 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
79 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
82 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
83 // This is currently broken as Google have disabled the KML output in Google Maps API v3
84 // It has been ifdefed out in the hope that Route Finding functionality will be restored one day...
85 // Only have 'JSON' and 'XML' see:
86 // https://developers.google.com/maps/documentation/directions/#DirectionsResponses
87 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
90 #define VIK_TRW_LAYER_TRACK_GC 16
91 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
92 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
93 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
94 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
95 #define VIK_TRW_LAYER_TRACK_GC_SLOW 13
96 #define VIK_TRW_LAYER_TRACK_GC_AVER 14
97 #define VIK_TRW_LAYER_TRACK_GC_FAST 15
99 #define DRAWMODE_BY_TRACK 0
100 #define DRAWMODE_BY_SPEED 1
101 #define DRAWMODE_ALL_BLACK 2
102 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
103 // as we are (re)calculating the colour for every point
108 /* this is how it knows when you click if you are clicking close to a trackpoint. */
109 #define TRACKPOINT_SIZE_APPROX 5
110 #define WAYPOINT_SIZE_APPROX 5
112 #define MIN_STOP_LENGTH 15
113 #define MAX_STOP_LENGTH 86400
114 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
115 /* this is multiplied by user-inputted value from 1-100. */
117 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
119 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
121 FS_XX_SMALL = 0, // 'xx-small'
124 FS_MEDIUM, // DEFAULT
131 struct _VikTrwLayer {
134 GHashTable *tracks_iters;
135 GHashTable *waypoints_iters;
136 GHashTable *waypoints;
137 GtkTreeIter waypoints_iter, tracks_iter;
138 gboolean tracks_visible, waypoints_visible;
141 guint8 drawpoints_size;
142 guint8 drawelevation;
143 guint8 elevation_factor;
147 guint8 drawdirections;
148 guint8 drawdirections_size;
149 guint8 line_thickness;
150 guint8 bg_line_thickness;
154 gboolean wp_draw_symbols;
155 font_size_t wp_font_size;
157 gdouble track_draw_speed_factor;
159 GdkGC *current_track_gc;
160 // Separate GC for a track's potential new point as drawn via separate method
161 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
162 GdkGC *current_track_newpoint_gc;
165 GdkGC *waypoint_text_gc;
166 GdkGC *waypoint_bg_gc;
167 VikTrack *current_track;
168 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
169 gboolean draw_sync_done;
170 gboolean draw_sync_do;
172 VikCoordMode coord_mode;
174 /* wp editing tool */
175 VikWaypoint *current_wp;
176 gpointer current_wp_id;
178 gboolean waypoint_rightclick;
180 /* track editing tool */
182 VikTrack *current_tp_track;
183 gpointer current_tp_id;
184 VikTrwLayerTpwin *tpwin;
186 /* track editing tool -- more specifically, moving tps */
189 /* route finder tool */
190 gboolean route_finder_started;
191 VikCoord route_finder_coord;
192 gboolean route_finder_check_added_track;
193 VikTrack *route_finder_added_track;
194 VikTrack *route_finder_current_track;
195 gboolean route_finder_append;
202 guint16 image_cache_size;
204 /* for waypoint text */
205 PangoLayout *wplabellayout;
207 gboolean has_verified_thumbnails;
209 GtkMenu *wp_right_click_menu;
210 GtkMenu *track_right_click_menu;
213 VikStdLayerMenuItem menu_selection;
215 gint highest_wp_number;
218 /* A caached waypoint image. */
221 gchar *image; /* filename */
224 struct DrawingParams {
228 guint16 width, height;
229 gdouble cc; // Cosine factor in track directions
230 gdouble ss; // Sine factor in track directions
231 const VikCoord *center;
233 gboolean one_zone, lat_lon;
234 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 gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
244 static void trw_layer_find_maxmin_tracks ( const gchar *name, 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 gchar *name, VikTrack *track, struct DrawingParams *dp );
251 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
253 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
254 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
255 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
256 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
257 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
258 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
259 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
260 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
261 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
262 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
263 static void trw_layer_append_track ( gpointer pass_along[6] );
264 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
265 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
266 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
267 static void trw_layer_split_segments ( gpointer pass_along[6] );
268 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
269 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
270 static void trw_layer_reverse ( gpointer pass_along[6] );
271 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
272 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
273 static void trw_layer_show_picture ( gpointer pass_along[6] );
274 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
276 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
277 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
278 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
279 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
280 static void trw_layer_new_wp ( gpointer lav[2] );
281 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
282 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
283 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
284 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
285 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
286 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
287 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
288 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
289 #ifdef VIK_CONFIG_GEOTAG
290 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
291 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
292 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
293 static void trw_layer_geotagging ( gpointer lav[2] );
295 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
296 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
297 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
299 #ifdef VIK_CONFIG_OPENSTREETMAP
300 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
302 #ifdef VIK_CONFIG_GEOCACHES
303 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
305 #ifdef VIK_CONFIG_GEOTAG
306 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
308 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
309 static void trw_layer_gps_upload ( gpointer lav[2] );
312 static void trw_layer_properties_item ( gpointer pass_along[7] );
313 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
314 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
316 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
317 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
318 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
320 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
321 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
322 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
323 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
325 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
326 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
327 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
328 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
329 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
330 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
331 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
332 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
333 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
334 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
335 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
336 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
337 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
338 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
339 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
340 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
341 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
342 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
343 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
344 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
345 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
346 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
349 static void cached_pixbuf_free ( CachedPixbuf *cp );
350 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
352 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
353 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
355 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
356 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
358 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
359 static void highest_wp_number_reset(VikTrwLayer *vtl);
360 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
361 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
363 // Note for the following tool GtkRadioActionEntry texts:
364 // the very first text value is an internal name not displayed anywhere
365 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
366 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
367 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
368 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
369 static VikToolInterface trw_layer_tools[] = {
370 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
371 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
372 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
374 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
376 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
377 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
378 (VikToolMouseFunc) tool_new_track_click,
379 (VikToolMouseMoveFunc) tool_new_track_move,
380 (VikToolMouseFunc) tool_new_track_release,
381 (VikToolKeyFunc) tool_new_track_key_press,
382 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
383 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
385 { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "<control><shift>B", N_("Begin Track"), 0 },
386 (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
387 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL,
389 GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
391 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
392 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
393 (VikToolMouseFunc) tool_edit_waypoint_click,
394 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
395 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
397 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
399 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
400 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
401 (VikToolMouseFunc) tool_edit_trackpoint_click,
402 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
403 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
405 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
407 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
408 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
409 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
411 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
413 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
414 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
415 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
416 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
418 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
421 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
423 /****** PARAMETERS ******/
425 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
426 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
428 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
429 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
431 #define MIN_POINT_SIZE 2
432 #define MAX_POINT_SIZE 10
434 #define MIN_ARROW_SIZE 3
435 #define MAX_ARROW_SIZE 20
437 static VikLayerParamScale params_scales[] = {
438 /* min max step digits */
439 { 1, 10, 1, 0 }, /* line_thickness */
440 { 0, 100, 1, 0 }, /* track draw speed factor */
441 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
442 /* 5 * step == how much to turn */
443 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
444 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
445 { 5, 500, 5, 0 }, // 5: image cache_size - " "
446 { 0, 8, 1, 0 }, // 6: Background line thickness
447 { 1, 64, 1, 0 }, /* wpsize */
448 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
449 { 1, 100, 1, 0 }, // 9: elevation factor
450 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
451 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
454 static gchar* params_font_sizes[] = {
455 N_("Extra Extra Small"),
461 N_("Extra Extra Large"),
464 VikLayerParam trw_layer_params[] = {
465 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
466 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
468 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
469 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
470 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
471 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
472 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
473 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
474 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
475 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
476 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
478 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
479 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
481 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
482 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
483 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
485 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
486 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
487 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
488 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
489 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
490 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
491 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
492 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
493 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
495 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
496 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
497 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
498 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
501 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
503 // Sublayer visibilities
540 *** 1) Add to trw_layer_params and enumeration
541 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
544 /****** END PARAMETERS ******/
546 static VikTrwLayer* trw_layer_new ( gint drawmode );
547 /* Layer Interface function definitions */
548 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
549 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
550 static void trw_layer_free ( VikTrwLayer *trwlayer );
551 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
552 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
553 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
554 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
555 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
556 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
557 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
558 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
559 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
560 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
561 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
562 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
563 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
564 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
565 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
566 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
567 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
568 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
569 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
570 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
571 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
572 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
573 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
574 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
575 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
576 /* End Layer Interface function definitions */
578 VikLayerInterface vik_trw_layer_interface = {
585 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
589 params_groups, /* params_groups */
590 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
594 (VikLayerFuncCreate) trw_layer_create,
595 (VikLayerFuncRealize) trw_layer_realize,
596 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
597 (VikLayerFuncFree) trw_layer_free,
599 (VikLayerFuncProperties) NULL,
600 (VikLayerFuncDraw) trw_layer_draw,
601 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
603 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
604 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
606 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
607 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
609 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
610 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
611 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
612 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
613 (VikLayerFuncLayerSelected) trw_layer_selected,
615 (VikLayerFuncMarshall) trw_layer_marshall,
616 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
618 (VikLayerFuncSetParam) trw_layer_set_param,
619 (VikLayerFuncGetParam) trw_layer_get_param,
621 (VikLayerFuncReadFileData) a_gpspoint_read_file,
622 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
624 (VikLayerFuncDeleteItem) trw_layer_del_item,
625 (VikLayerFuncCutItem) trw_layer_cut_item,
626 (VikLayerFuncCopyItem) trw_layer_copy_item,
627 (VikLayerFuncPasteItem) trw_layer_paste_item,
628 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
630 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
632 (VikLayerFuncSelectClick) trw_layer_select_click,
633 (VikLayerFuncSelectMove) trw_layer_select_move,
634 (VikLayerFuncSelectRelease) trw_layer_select_release,
635 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
644 GType vik_trw_layer_get_type ()
646 static GType vtl_type = 0;
650 static const GTypeInfo vtl_info =
652 sizeof (VikTrwLayerClass),
653 NULL, /* base_init */
654 NULL, /* base_finalize */
655 NULL, /* class init */
656 NULL, /* class_finalize */
657 NULL, /* class_data */
658 sizeof (VikTrwLayer),
660 NULL /* instance init */
662 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
668 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
670 static gpointer pass_along[6];
676 pass_along[1] = NULL;
677 pass_along[2] = GINT_TO_POINTER (subtype);
678 pass_along[3] = sublayer;
679 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
680 pass_along[5] = NULL;
682 trw_layer_delete_item ( pass_along );
685 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
687 static gpointer pass_along[6];
693 pass_along[1] = NULL;
694 pass_along[2] = GINT_TO_POINTER (subtype);
695 pass_along[3] = sublayer;
696 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
697 pass_along[5] = NULL;
699 trw_layer_copy_item_cb(pass_along);
700 trw_layer_cut_item_cb(pass_along);
703 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
705 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
706 gint subtype = GPOINTER_TO_INT (pass_along[2]);
707 gpointer * sublayer = pass_along[3];
711 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
715 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
716 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
717 if ( wp && wp->name )
720 name = NULL; // Broken :(
723 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
724 if ( trk && trk->name )
727 name = NULL; // Broken :(
730 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
731 subtype, len, name, data);
735 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
737 trw_layer_copy_item_cb(pass_along);
738 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
739 trw_layer_delete_item(pass_along);
742 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
753 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
755 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
756 // 'Simple' memory copy of byte array from the marshalling above
757 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
758 fi = g_malloc ( *len );
760 memcpy(fi->data, id, il);
763 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
764 // less magic than before...
765 *len = sizeof(FlatItem) + 1 + il;
766 fi = g_malloc ( *len );
768 memcpy(fi->data, id, il);
772 *item = (guint8 *)fi;
775 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
777 FlatItem *fi = (FlatItem *) item;
779 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
784 w = vik_waypoint_unmarshall(fi->data, fi->len);
785 // When copying - we'll create a new name based on the original
786 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
787 vik_trw_layer_add_waypoint ( vtl, name, w );
788 waypoint_convert (NULL, w, &vtl->coord_mode);
790 // Consider if redraw necessary for the new item
791 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
792 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
795 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
800 t = vik_track_unmarshall(fi->data, fi->len);
801 // When copying - we'll create a new name based on the original
802 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
803 vik_trw_layer_add_track ( vtl, name, t );
804 track_convert (name, t, &vtl->coord_mode);
806 // Consider if redraw necessary for the new item
807 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
808 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
814 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
821 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
825 case PARAM_TV: vtl->tracks_visible = data.b; break;
826 case PARAM_WV: vtl->waypoints_visible = data.b; break;
827 case PARAM_DM: vtl->drawmode = data.u; break;
828 case PARAM_DP: vtl->drawpoints = data.b; break;
830 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
831 vtl->drawpoints_size = data.u;
833 case PARAM_DE: vtl->drawelevation = data.b; break;
834 case PARAM_DS: vtl->drawstops = data.b; break;
835 case PARAM_DL: vtl->drawlines = data.b; break;
836 case PARAM_DD: vtl->drawdirections = data.b; break;
838 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
839 vtl->drawdirections_size = data.u;
841 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
842 vtl->stop_length = data.u;
844 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
845 vtl->elevation_factor = data.u;
847 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
849 vtl->line_thickness = data.u;
850 trw_layer_new_track_gcs ( vtl, vp );
853 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
855 vtl->bg_line_thickness = data.u;
856 trw_layer_new_track_gcs ( vtl, vp );
859 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
860 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
861 case PARAM_DLA: vtl->drawlabels = data.b; break;
862 case PARAM_DI: vtl->drawimages = data.b; break;
863 case PARAM_IS: if ( data.u != vtl->image_size )
865 vtl->image_size = data.u;
866 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
867 g_queue_free ( vtl->image_cache );
868 vtl->image_cache = g_queue_new ();
871 case PARAM_IA: vtl->image_alpha = data.u; break;
872 case PARAM_ICS: vtl->image_cache_size = data.u;
873 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
874 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
876 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
877 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
878 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
879 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
880 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
881 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
882 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
883 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
888 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
890 VikLayerParamData rv;
893 case PARAM_TV: rv.b = vtl->tracks_visible; break;
894 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
895 case PARAM_DM: rv.u = vtl->drawmode; break;
896 case PARAM_DP: rv.b = vtl->drawpoints; break;
897 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
898 case PARAM_DE: rv.b = vtl->drawelevation; break;
899 case PARAM_EF: rv.u = vtl->elevation_factor; break;
900 case PARAM_DS: rv.b = vtl->drawstops; break;
901 case PARAM_SL: rv.u = vtl->stop_length; break;
902 case PARAM_DL: rv.b = vtl->drawlines; break;
903 case PARAM_DD: rv.b = vtl->drawdirections; break;
904 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
905 case PARAM_LT: rv.u = vtl->line_thickness; break;
906 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
907 case PARAM_DLA: rv.b = vtl->drawlabels; break;
908 case PARAM_DI: rv.b = vtl->drawimages; break;
909 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
910 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
911 case PARAM_IS: rv.u = vtl->image_size; break;
912 case PARAM_IA: rv.u = vtl->image_alpha; break;
913 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
914 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
915 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
916 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
917 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
918 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
919 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
920 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
921 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
926 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
937 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
938 a_gpx_write_file(vtl, f, NULL);
939 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
942 g_file_get_contents(tmpname, &dd, &dl, NULL);
943 *len = sizeof(pl) + pl + dl;
944 *data = g_malloc(*len);
945 memcpy(*data, &pl, sizeof(pl));
946 memcpy(*data + sizeof(pl), pd, pl);
947 memcpy(*data + sizeof(pl) + pl, dd, dl);
956 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
958 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
964 memcpy(&pl, data, sizeof(pl));
966 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
969 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
970 g_critical("couldn't open temp file");
973 fwrite(data, len - pl - sizeof(pl), 1, f);
975 a_gpx_read_file(rv, f);
983 // Keep interesting hash function at least visible
985 static guint strcase_hash(gconstpointer v)
987 // 31 bit hash function
990 gchar s[128]; // malloc is too slow for reading big files
993 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
994 p[i] = toupper(t[i]);
1000 for (p += 1; *p != '\0'; p++)
1001 h = (h << 5) - h + *p;
1008 static VikTrwLayer* trw_layer_new ( gint drawmode )
1010 if (trw_layer_params[PARAM_DM].widget_data == NULL)
1011 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
1012 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
1013 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
1015 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1016 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1018 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1019 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1021 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1022 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1023 // and with normal PC processing capabilities - it has negligibile performance impact
1024 // This also minimized the amount of rework - as the management of the hash tables already exists.
1026 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1027 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1028 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1030 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1031 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1032 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1033 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1035 /* TODO: constants at top */
1036 rv->waypoints_visible = rv->tracks_visible = TRUE;
1037 rv->drawmode = drawmode;
1038 rv->drawpoints = TRUE;
1039 rv->drawpoints_size = MIN_POINT_SIZE;
1040 rv->drawdirections_size = 5;
1041 rv->drawstops = FALSE;
1042 rv->drawelevation = FALSE;
1043 rv->elevation_factor = 30;
1044 rv->stop_length = 60;
1045 rv->drawlines = TRUE;
1046 rv->wplabellayout = NULL;
1047 rv->wp_right_click_menu = NULL;
1048 rv->track_right_click_menu = NULL;
1049 rv->waypoint_gc = NULL;
1050 rv->waypoint_text_gc = NULL;
1051 rv->waypoint_bg_gc = NULL;
1052 rv->track_gc = NULL;
1053 rv->track_draw_speed_factor = 30.0;
1054 rv->line_thickness = 1;
1055 rv->bg_line_thickness = 0;
1056 rv->current_wp = NULL;
1057 rv->current_wp_id = NULL;
1058 rv->current_track = NULL;
1059 rv->current_tpl = NULL;
1060 rv->current_tp_track = NULL;
1061 rv->current_tp_id = NULL;
1062 rv->moving_tp = FALSE;
1063 rv->moving_wp = FALSE;
1065 rv->draw_sync_done = TRUE;
1066 rv->draw_sync_do = TRUE;
1068 rv->route_finder_started = FALSE;
1069 rv->route_finder_check_added_track = FALSE;
1070 rv->route_finder_current_track = NULL;
1071 rv->route_finder_append = FALSE;
1073 rv->waypoint_rightclick = FALSE;
1075 rv->image_cache = g_queue_new();
1076 rv->image_size = 64;
1077 rv->image_alpha = 255;
1078 rv->image_cache_size = 300;
1079 rv->drawimages = TRUE;
1080 rv->drawlabels = TRUE;
1085 static void trw_layer_free ( VikTrwLayer *trwlayer )
1087 g_hash_table_destroy(trwlayer->waypoints);
1088 g_hash_table_destroy(trwlayer->tracks);
1090 /* ODC: replace with GArray */
1091 trw_layer_free_track_gcs ( trwlayer );
1093 if ( trwlayer->wp_right_click_menu )
1094 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1096 if ( trwlayer->track_right_click_menu )
1097 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1099 if ( trwlayer->wplabellayout != NULL)
1100 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1102 if ( trwlayer->waypoint_gc != NULL )
1103 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1105 if ( trwlayer->waypoint_text_gc != NULL )
1106 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1108 if ( trwlayer->waypoint_bg_gc != NULL )
1109 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1111 if ( trwlayer->tpwin != NULL )
1112 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1114 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1115 g_queue_free ( trwlayer->image_cache );
1118 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1122 dp->xmpp = vik_viewport_get_xmpp ( vp );
1123 dp->ympp = vik_viewport_get_ympp ( vp );
1124 dp->width = vik_viewport_get_width ( vp );
1125 dp->height = vik_viewport_get_height ( vp );
1126 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1127 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1129 dp->center = vik_viewport_get_center ( vp );
1130 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1131 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1136 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1137 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1138 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1140 dp->ce1 = dp->center->east_west-w2;
1141 dp->ce2 = dp->center->east_west+w2;
1142 dp->cn1 = dp->center->north_south-h2;
1143 dp->cn2 = dp->center->north_south+h2;
1144 } else if ( dp->lat_lon ) {
1145 VikCoord upperleft, bottomright;
1146 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1147 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1148 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1149 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1150 dp->ce1 = upperleft.east_west;
1151 dp->ce2 = bottomright.east_west;
1152 dp->cn1 = bottomright.north_south;
1153 dp->cn2 = upperleft.north_south;
1156 dp->track_gc_iter = 0;
1160 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1161 * Here a simple traffic like light colour system is used:
1162 * . slow points are red
1163 * . average is yellow
1164 * . fast points are green
1166 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1169 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1170 if ( average_speed > 0 ) {
1171 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1172 if ( rv < low_speed )
1173 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1174 else if ( rv > high_speed )
1175 return VIK_TRW_LAYER_TRACK_GC_FAST;
1177 return VIK_TRW_LAYER_TRACK_GC_AVER;
1180 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1183 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1185 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1186 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1187 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1188 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1191 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1193 /* TODO: this function is a mess, get rid of any redundancy */
1194 GList *list = track->trackpoints;
1196 gboolean useoldvals = TRUE;
1198 gboolean drawpoints;
1200 gboolean drawelevation;
1201 gdouble min_alt, max_alt, alt_diff = 0;
1203 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1204 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1207 if ( dp->vtl->drawelevation )
1209 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1210 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1211 alt_diff = max_alt - min_alt;
1214 if ( ! track->visible )
1217 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1218 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1219 trw_layer_draw_track ( name, track, dp, TRUE );
1221 if ( drawing_white_background )
1222 drawpoints = drawstops = FALSE;
1224 drawpoints = dp->vtl->drawpoints;
1225 drawstops = dp->vtl->drawstops;
1228 gboolean drawing_highlight = FALSE;
1229 /* Current track - used for creation */
1230 if ( track == dp->vtl->current_track )
1231 main_gc = dp->vtl->current_track_gc;
1233 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1234 /* Draw all tracks of the layer in special colour */
1235 /* if track is member of selected layer or is the current selected track
1236 then draw in the highlight colour.
1237 NB this supercedes the drawmode */
1238 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1239 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1240 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1241 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1242 drawing_highlight = TRUE;
1245 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1246 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1248 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1252 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1253 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1255 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1260 int x, y, oldx, oldy;
1261 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1263 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1265 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1267 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1269 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1270 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1276 gdouble average_speed = 0.0;
1277 gdouble low_speed = 0.0;
1278 gdouble high_speed = 0.0;
1279 // If necessary calculate these values - which is done only once per track redraw
1280 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1281 // the percentage factor away from the average speed determines transistions between the levels
1282 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1283 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1284 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1287 while ((list = g_list_next(list)))
1289 tp = VIK_TRACKPOINT(list->data);
1290 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1292 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1293 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1294 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1295 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1296 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1298 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1301 * If points are the same in display coordinates, don't draw.
1303 if ( useoldvals && x == oldx && y == oldy )
1305 // Still need to process points to ensure 'stops' are drawn if required
1306 if ( drawstops && drawpoints && ! drawing_white_background && list->next &&
1307 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1308 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 );
1313 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1314 if ( drawpoints || dp->vtl->drawlines ) {
1315 // setup main_gc for both point and line drawing
1316 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1317 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1318 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1322 if ( drawpoints && ! drawing_white_background )
1327 * The concept of drawing stops is that a trackpoint
1328 * that is if the next trackpoint has a timestamp far into
1329 * the future, we draw a circle of 6x trackpoint size,
1330 * instead of a rectangle of 2x trackpoint size.
1331 * This is drawn first so the trackpoint will be drawn on top
1334 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1335 /* Stop point. Draw 6x circle. */
1336 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 );
1338 /* Regular point - draw 2x square. */
1339 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1342 /* Final point - draw 4x circle. */
1343 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 );
1346 if ((!tp->newsegment) && (dp->vtl->drawlines))
1349 /* UTM only: zone check */
1350 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1351 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1354 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1356 if ( drawing_white_background ) {
1357 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1361 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1363 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1365 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1370 tmp[1].y = oldy-FIXALTITUDE(list->data);
1372 tmp[2].y = y-FIXALTITUDE(list->next->data);
1377 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1378 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1380 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1381 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1383 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1388 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1389 // Draw an arrow at the mid point to show the direction of the track
1390 // Code is a rework from vikwindow::draw_ruler()
1391 gint midx = (oldx + x) / 2;
1392 gint midy = (oldy + y) / 2;
1394 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1395 // Avoid divide by zero and ensure at least 1 pixel big
1397 gdouble dx = (oldx - midx) / len;
1398 gdouble dy = (oldy - midy) / len;
1399 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1400 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1410 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1412 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1413 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1415 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1417 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1418 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1419 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1423 * If points are the same in display coordinates, don't draw.
1425 if ( x != oldx || y != oldy )
1427 if ( drawing_white_background )
1428 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1430 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1436 * If points are the same in display coordinates, don't draw.
1438 if ( x != oldx && y != oldy )
1440 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1441 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1449 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1450 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
1451 dp->track_gc_iter = 0;
1454 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1455 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1457 trw_layer_draw_track ( name, track, dp, FALSE );
1460 static void cached_pixbuf_free ( CachedPixbuf *cp )
1462 g_object_unref ( G_OBJECT(cp->pixbuf) );
1463 g_free ( cp->image );
1466 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1468 return strcmp ( cp->image, name );
1471 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1474 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1475 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1476 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1479 GdkPixbuf *sym = NULL;
1480 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1482 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1484 if ( wp->image && dp->vtl->drawimages )
1486 GdkPixbuf *pixbuf = NULL;
1489 if ( dp->vtl->image_alpha == 0)
1492 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1494 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1497 gchar *image = wp->image;
1498 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1499 if ( ! regularthumb )
1501 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1502 image = "\x12\x00"; /* this shouldn't occur naturally. */
1506 CachedPixbuf *cp = NULL;
1507 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1508 if ( dp->vtl->image_size == 128 )
1509 cp->pixbuf = regularthumb;
1512 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1513 g_assert ( cp->pixbuf );
1514 g_object_unref ( G_OBJECT(regularthumb) );
1516 cp->image = g_strdup ( image );
1518 /* needed so 'click picture' tool knows how big the pic is; we don't
1519 * store it in cp because they may have been freed already. */
1520 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1521 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1523 g_queue_push_head ( dp->vtl->image_cache, cp );
1524 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1525 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1527 pixbuf = cp->pixbuf;
1531 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1537 w = gdk_pixbuf_get_width ( pixbuf );
1538 h = gdk_pixbuf_get_height ( pixbuf );
1540 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1542 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1543 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1544 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1545 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1546 // Highlighted - so draw a little border around the chosen one
1547 // single line seems a little weak so draw 2 of them
1548 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1549 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1550 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1551 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1554 if ( dp->vtl->image_alpha == 255 )
1555 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1557 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1559 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1563 /* DRAW ACTUAL DOT */
1564 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1565 vik_viewport_draw_pixbuf ( dp->vp, sym, 0, 0, x - gdk_pixbuf_get_width(sym)/2, y - gdk_pixbuf_get_height(sym)/2, -1, -1 );
1567 else if ( wp == dp->vtl->current_wp ) {
1568 switch ( dp->vtl->wp_symbol ) {
1569 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;
1570 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;
1571 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;
1572 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 );
1573 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 );
1577 switch ( dp->vtl->wp_symbol ) {
1578 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;
1579 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;
1580 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;
1581 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 );
1582 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;
1586 if ( dp->vtl->drawlabels )
1588 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1589 gint label_x, label_y;
1591 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1593 // Could this stored in the waypoint rather than recreating each pass?
1594 gchar *fsize = NULL;
1595 switch (dp->vtl->wp_font_size) {
1596 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1597 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1598 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1599 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1600 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1601 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1602 default: fsize = g_strdup ( "medium" ); break;
1605 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1607 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1608 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1610 // Fallback if parse failure
1611 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1613 g_free ( wp_label_markup );
1616 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1617 label_x = x - width/2;
1619 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1621 label_y = y - dp->vtl->wp_size - height - 2;
1623 /* if highlight mode on, then draw background text in highlight colour */
1624 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1625 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1626 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1627 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1628 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1630 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1633 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1635 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1640 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1642 static struct DrawingParams dp;
1643 g_assert ( l != NULL );
1645 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1647 if ( l->tracks_visible )
1648 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1650 if (l->waypoints_visible)
1651 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1654 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1657 if ( vtl->track_bg_gc )
1659 g_object_unref ( vtl->track_bg_gc );
1660 vtl->track_bg_gc = NULL;
1662 if ( vtl->current_track_gc )
1664 g_object_unref ( vtl->current_track_gc );
1665 vtl->current_track_gc = NULL;
1667 if ( vtl->current_track_newpoint_gc )
1669 g_object_unref ( vtl->current_track_newpoint_gc );
1670 vtl->current_track_newpoint_gc = NULL;
1673 if ( ! vtl->track_gc )
1675 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1676 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1677 g_array_free ( vtl->track_gc, TRUE );
1678 vtl->track_gc = NULL;
1681 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1683 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1684 gint width = vtl->line_thickness;
1686 if ( vtl->track_gc )
1687 trw_layer_free_track_gcs ( vtl );
1689 if ( vtl->track_bg_gc )
1690 g_object_unref ( vtl->track_bg_gc );
1691 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1693 // Ensure new track drawing heeds line thickness setting
1694 // however always have a minium of 2, as 1 pixel is really narrow
1695 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1697 if ( vtl->current_track_gc )
1698 g_object_unref ( vtl->current_track_gc );
1699 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1700 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1702 // 'newpoint' gc is exactly the same as the current track gc
1703 if ( vtl->current_track_newpoint_gc )
1704 g_object_unref ( vtl->current_track_newpoint_gc );
1705 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1706 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1708 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1710 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1712 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1713 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1714 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1715 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1716 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1717 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1718 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1719 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1720 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1721 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1723 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1725 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1727 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1728 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1729 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1731 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1734 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1736 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1737 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1739 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1740 /* early exit, as the rest is GUI related */
1744 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1745 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1747 trw_layer_new_track_gcs ( rv, vp );
1749 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1750 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1751 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1752 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1754 rv->has_verified_thumbnails = FALSE;
1755 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1757 rv->wp_draw_symbols = TRUE;
1758 rv->wp_font_size = FS_MEDIUM;
1760 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1762 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1767 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1769 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1771 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1772 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]), NULL, TRUE, TRUE );
1774 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]), NULL, TRUE, TRUE );
1777 *new_iter = *((GtkTreeIter *) pass_along[1]);
1778 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1780 if ( ! track->visible )
1781 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1784 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1786 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1787 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1788 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]), NULL, TRUE, TRUE );
1790 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]), NULL, TRUE, TRUE );
1793 *new_iter = *((GtkTreeIter *) pass_along[1]);
1794 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1796 if ( ! wp->visible )
1797 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1801 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1804 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1806 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1807 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1809 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1811 if ( ! vtl->tracks_visible )
1812 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1814 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1816 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1817 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1819 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1822 if ( ! vtl->waypoints_visible )
1823 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1825 pass_along[0] = &(vtl->waypoints_iter);
1826 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1828 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1832 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1836 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1837 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1838 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1840 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1842 return (t->visible ^= 1);
1846 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1848 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1850 return (t->visible ^= 1);
1859 * Return a property about tracks for this layer
1861 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1863 return vtl->line_thickness;
1866 // Structure to hold multiple track information for a layer
1875 * Build up layer multiple track information via updating the tooltip_tracks structure
1877 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1879 tt->length = tt->length + vik_track_get_length (tr);
1881 // Ensure times are available
1882 if ( tr->trackpoints &&
1883 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1884 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1887 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1888 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1890 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1891 // Hence initialize to the first 'proper' value
1892 if ( tt->start_time == 0 )
1893 tt->start_time = t1;
1894 if ( tt->end_time == 0 )
1897 // Update find the earliest / last times
1898 if ( t1 < tt->start_time )
1899 tt->start_time = t1;
1900 if ( t2 > tt->end_time )
1903 // Keep track of total time
1904 // there maybe gaps within a track (eg segments)
1905 // but this should be generally good enough for a simple indicator
1906 tt->duration = tt->duration + (int)(t2-t1);
1911 * Generate tooltip text for the layer.
1912 * This is relatively complicated as it considers information for
1913 * no tracks, a single track or multiple tracks
1914 * (which may or may not have timing information)
1916 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1927 static gchar tmp_buf[128];
1930 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1932 // Safety check - I think these should always be valid
1933 if ( vtl->tracks && vtl->waypoints ) {
1934 tooltip_tracks tt = { 0.0, 0, 0 };
1935 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1937 GDate* gdate_start = g_date_new ();
1938 g_date_set_time_t (gdate_start, tt.start_time);
1940 GDate* gdate_end = g_date_new ();
1941 g_date_set_time_t (gdate_end, tt.end_time);
1943 if ( g_date_compare (gdate_start, gdate_end) ) {
1944 // Dates differ so print range on separate line
1945 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1946 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1947 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1950 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1951 if ( tt.start_time != 0 )
1952 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1956 if ( tt.length > 0.0 ) {
1957 gdouble len_in_units;
1959 // Setup info dependent on distance units
1960 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1961 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1962 len_in_units = VIK_METERS_TO_MILES(tt.length);
1965 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1966 len_in_units = tt.length/1000.0;
1969 // Timing information if available
1971 if ( tt.duration > 0 ) {
1972 g_snprintf (tbuf1, sizeof(tbuf1),
1973 _(" in %d:%02d hrs:mins"),
1974 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1976 g_snprintf (tbuf2, sizeof(tbuf2),
1977 _("\n%sTotal Length %.1f %s%s"),
1978 tbuf3, len_in_units, tbuf4, tbuf1);
1981 // Put together all the elements to form compact tooltip text
1982 g_snprintf (tmp_buf, sizeof(tmp_buf),
1983 _("Tracks: %d - Waypoints: %d%s"),
1984 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1986 g_date_free (gdate_start);
1987 g_date_free (gdate_end);
1994 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1998 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1999 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
2000 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2002 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
2004 // Could be a better way of handling strings - but this works...
2005 gchar time_buf1[20];
2006 gchar time_buf2[20];
2007 time_buf1[0] = '\0';
2008 time_buf2[0] = '\0';
2009 static gchar tmp_buf[100];
2010 // Compact info: Short date eg (11/20/99), duration and length
2011 // Hopefully these are the things that are most useful and so promoted into the tooltip
2012 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2013 // %x The preferred date representation for the current locale without the time.
2014 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2015 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2016 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2018 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2021 // Get length and consider the appropriate distance units
2022 gdouble tr_len = vik_track_get_length(tr);
2023 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2024 switch (dist_units) {
2025 case VIK_UNITS_DISTANCE_KILOMETRES:
2026 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2028 case VIK_UNITS_DISTANCE_MILES:
2029 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2038 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2040 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2041 // NB It's OK to return NULL
2052 * Function to show basic track point information on the statusbar
2054 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2057 switch (a_vik_get_units_height ()) {
2058 case VIK_UNITS_HEIGHT_FEET:
2059 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2062 //VIK_UNITS_HEIGHT_METRES:
2063 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2068 if ( trkpt->has_timestamp ) {
2069 // Compact date time format
2070 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2074 // Position is put later on, as this bit may not be seen if the display is not big enough,
2075 // one can easily use the current pointer position to see this if needed
2076 gchar *lat = NULL, *lon = NULL;
2077 static struct LatLon ll;
2078 vik_coord_to_latlon (&(trkpt->coord), &ll);
2079 a_coords_latlon_to_string ( &ll, &lat, &lon );
2082 // Again is put later on, as this bit may not be seen if the display is not big enough
2083 // trackname can be seen from the treeview (when enabled)
2084 // Also name could be very long to not leave room for anything else
2087 if ( vtl->current_tp_track ) {
2088 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2091 // Combine parts to make overall message
2092 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2093 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2100 * Function to show basic waypoint information on the statusbar
2102 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2105 switch (a_vik_get_units_height ()) {
2106 case VIK_UNITS_HEIGHT_FEET:
2107 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2110 //VIK_UNITS_HEIGHT_METRES:
2111 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2115 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2116 // one can easily use the current pointer position to see this if needed
2117 gchar *lat = NULL, *lon = NULL;
2118 static struct LatLon ll;
2119 vik_coord_to_latlon (&(wpt->coord), &ll);
2120 a_coords_latlon_to_string ( &ll, &lat, &lon );
2122 // Combine parts to make overall message
2125 // Add comment if available
2126 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2128 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2129 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2136 * General layer selection function, find out which bit is selected and take appropriate action
2138 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2141 l->current_wp = NULL;
2142 l->current_wp_id = NULL;
2143 trw_layer_cancel_current_tp ( l, FALSE );
2146 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2150 case VIK_TREEVIEW_TYPE_LAYER:
2152 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2153 /* Mark for redraw */
2158 case VIK_TREEVIEW_TYPE_SUBLAYER:
2162 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2164 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2165 /* Mark for redraw */
2169 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2171 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2172 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2173 /* Mark for redraw */
2177 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2179 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2180 /* Mark for redraw */
2184 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2186 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2188 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2189 // Show some waypoint info
2190 set_statusbar_msg_info_wpt ( l, wpt );
2191 /* Mark for redraw */
2198 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2207 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2212 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2217 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2219 return l->waypoints;
2223 * ATM use a case sensitive find
2224 * Finds the first one
2226 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2228 if ( wp && wp->name )
2229 if ( ! strcmp ( wp->name, name ) )
2235 * Get waypoint by name - not guaranteed to be unique
2236 * Finds the first one
2238 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2240 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2244 * ATM use a case sensitive find
2245 * Finds the first one
2247 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2249 if ( trk && trk->name )
2250 if ( ! strcmp ( trk->name, name ) )
2256 * Get track by name - not guaranteed to be unique
2257 * Finds the first one
2259 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2261 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2264 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2266 static VikCoord fixme;
2267 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2268 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2269 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2270 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2271 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2272 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2273 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2274 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2275 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2278 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2280 GList *tr = trk->trackpoints;
2281 static VikCoord fixme;
2285 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2286 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2287 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2288 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2289 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2290 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2291 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2292 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2293 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2298 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2300 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2301 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2303 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2304 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2305 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2306 maxmin[0].lat = wpt_maxmin[0].lat;
2309 maxmin[0].lat = trk_maxmin[0].lat;
2311 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2312 maxmin[0].lon = wpt_maxmin[0].lon;
2315 maxmin[0].lon = trk_maxmin[0].lon;
2317 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2318 maxmin[1].lat = wpt_maxmin[1].lat;
2321 maxmin[1].lat = trk_maxmin[1].lat;
2323 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2324 maxmin[1].lon = wpt_maxmin[1].lon;
2327 maxmin[1].lon = trk_maxmin[1].lon;
2331 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2333 /* 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... */
2334 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2335 trw_layer_find_maxmin (vtl, maxmin);
2336 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2340 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2341 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2346 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2349 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2350 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2352 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2355 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2357 /* First set the center [in case previously viewing from elsewhere] */
2358 /* Then loop through zoom levels until provided positions are in view */
2359 /* This method is not particularly fast - but should work well enough */
2360 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2362 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2363 vik_viewport_set_center_coord ( vvp, &coord );
2365 /* Convert into definite 'smallest' and 'largest' positions */
2366 struct LatLon minmin;
2367 if ( maxmin[0].lat < maxmin[1].lat )
2368 minmin.lat = maxmin[0].lat;
2370 minmin.lat = maxmin[1].lat;
2372 struct LatLon maxmax;
2373 if ( maxmin[0].lon > maxmin[1].lon )
2374 maxmax.lon = maxmin[0].lon;
2376 maxmax.lon = maxmin[1].lon;
2378 /* Never zoom in too far - generally not that useful, as too close ! */
2379 /* Always recalculate the 'best' zoom level */
2381 vik_viewport_set_zoom ( vvp, zoom );
2383 gdouble min_lat, max_lat, min_lon, max_lon;
2384 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2385 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2386 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2387 /* NB I think the logic used in this test to determine if the bounds is within view
2388 fails if track goes across 180 degrees longitude.
2389 Hopefully that situation is not too common...
2390 Mind you viking doesn't really do edge locations to well anyway */
2391 if ( min_lat < minmin.lat &&
2392 max_lat > minmin.lat &&
2393 min_lon < maxmax.lon &&
2394 max_lon > maxmax.lon )
2395 /* Found within zoom level */
2400 vik_viewport_set_zoom ( vvp, zoom );
2404 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2406 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2407 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2408 trw_layer_find_maxmin (vtl, maxmin);
2409 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2412 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2417 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2419 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])) ) ) {
2420 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2423 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2426 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2428 GtkWidget *file_selector;
2430 gboolean failed = FALSE;
2431 file_selector = gtk_file_chooser_dialog_new (title,
2433 GTK_FILE_CHOOSER_ACTION_SAVE,
2434 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2435 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2437 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2439 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2441 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2442 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2444 gtk_widget_hide ( file_selector );
2445 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2450 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2452 gtk_widget_hide ( file_selector );
2453 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2458 gtk_widget_destroy ( file_selector );
2460 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2463 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2465 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2468 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2470 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2473 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2475 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2476 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2477 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2478 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2480 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2482 g_free ( auto_save_name );
2485 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2487 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2488 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2489 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2490 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2492 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2494 g_free ( auto_save_name );
2498 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2501 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2503 gchar *name_used = NULL;
2506 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2507 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2509 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2513 gchar *quoted_file = g_shell_quote ( name_used );
2514 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2515 g_free ( quoted_file );
2516 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2518 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2519 g_error_free ( err );
2523 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2524 //g_remove ( name_used );
2525 // Perhaps should be deleted when the program ends?
2526 // For now leave it to the user to delete it / use system temp cleanup methods.
2527 g_free ( name_used );
2531 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2533 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2536 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2538 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2541 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2543 gpointer layer_and_vlp[2];
2544 layer_and_vlp[0] = pass_along[0];
2545 layer_and_vlp[1] = pass_along[1];
2546 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2548 if ( !trk || !trk->name )
2551 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2552 gchar *auto_save_name = g_strdup ( trk->name );
2553 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2554 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2556 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2558 g_free ( auto_save_name );
2562 VikWaypoint *wp; // input
2563 gpointer uuid; // output
2566 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2568 wpu_udata *user_data = udata;
2569 if ( wp == user_data->wp ) {
2570 user_data->uuid = id;
2576 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2578 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2579 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2580 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2582 GTK_RESPONSE_REJECT,
2584 GTK_RESPONSE_ACCEPT,
2587 GtkWidget *label, *entry;
2588 label = gtk_label_new(_("Waypoint Name:"));
2589 entry = gtk_entry_new();
2591 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2592 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2593 gtk_widget_show_all ( label );
2594 gtk_widget_show_all ( entry );
2596 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2598 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2600 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2601 // Find *first* wp with the given name
2602 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2605 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2608 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2609 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2611 // Find and select on the side panel
2616 // Hmmm, want key of it
2617 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2619 if ( wpf && udata.uuid ) {
2620 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2621 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2630 gtk_widget_destroy ( dia );
2633 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2635 gchar *default_name = highest_wp_number_get(vtl);
2636 VikWaypoint *wp = vik_waypoint_new();
2637 gchar *returned_name;
2639 wp->coord = *def_coord;
2641 // Attempt to auto set height if DEM data is available
2642 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2643 if ( elev != VIK_DEM_INVALID_ELEVATION )
2644 wp->altitude = (gdouble)elev;
2646 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2648 if ( returned_name )
2651 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2652 g_free (default_name);
2653 g_free (returned_name);
2656 g_free (default_name);
2657 vik_waypoint_free(wp);
2661 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2664 struct LatLon one_ll, two_ll;
2665 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2667 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2668 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2669 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2670 VikViewport *vvp = vik_window_viewport(vw);
2671 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2672 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2673 vik_coord_to_latlon(&one, &one_ll);
2674 vik_coord_to_latlon(&two, &two_ll);
2675 if (one_ll.lat > two_ll.lat) {
2676 maxmin[0].lat = one_ll.lat;
2677 maxmin[1].lat = two_ll.lat;
2680 maxmin[0].lat = two_ll.lat;
2681 maxmin[1].lat = one_ll.lat;
2683 if (one_ll.lon > two_ll.lon) {
2684 maxmin[0].lon = one_ll.lon;
2685 maxmin[1].lon = two_ll.lon;
2688 maxmin[0].lon = two_ll.lon;
2689 maxmin[1].lon = one_ll.lon;
2691 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2694 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2696 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2697 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2698 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2700 trw_layer_find_maxmin (vtl, maxmin);
2701 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2704 #ifdef VIK_CONFIG_GEOTAG
2705 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2707 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2709 // Update directly - not changing the mtime
2710 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2713 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2715 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2718 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2722 * Use code in separate file for this feature as reasonably complex
2724 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2726 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2727 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2728 // Unset so can be reverified later if necessary
2729 vtl->has_verified_thumbnails = FALSE;
2731 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2737 static void trw_layer_geotagging ( gpointer lav[2] )
2739 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2740 // Unset so can be reverified later if necessary
2741 vtl->has_verified_thumbnails = FALSE;
2743 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2750 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2753 * Acquire into this TRW Layer straight from GPS Device
2755 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2757 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2758 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2759 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2760 VikViewport *vvp = vik_window_viewport(vw);
2762 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2763 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2766 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2768 * Acquire into this TRW Layer from Google Directions
2770 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2772 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2773 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2774 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2775 VikViewport *vvp = vik_window_viewport(vw);
2777 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2781 #ifdef VIK_CONFIG_OPENSTREETMAP
2783 * Acquire into this TRW Layer from OSM
2785 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2787 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2788 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2789 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2790 VikViewport *vvp = vik_window_viewport(vw);
2792 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2796 #ifdef VIK_CONFIG_GEOCACHES
2798 * Acquire into this TRW Layer from Geocaching.com
2800 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2802 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2803 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2804 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2805 VikViewport *vvp = vik_window_viewport(vw);
2807 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2811 #ifdef VIK_CONFIG_GEOTAG
2813 * Acquire into this TRW Layer from images
2815 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2817 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2818 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2819 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2820 VikViewport *vvp = vik_window_viewport(vw);
2822 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2823 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2825 // Reverify thumbnails as they may have changed
2826 vtl->has_verified_thumbnails = FALSE;
2827 trw_layer_verify_thumbnails ( vtl, NULL );
2831 static void trw_layer_gps_upload ( gpointer lav[2] )
2833 gpointer pass_along[6];
2834 pass_along[0] = lav[0];
2835 pass_along[1] = lav[1];
2836 pass_along[2] = NULL; // No track - operate on the layer
2837 pass_along[3] = NULL;
2838 pass_along[4] = NULL;
2839 pass_along[5] = NULL;
2841 trw_layer_gps_upload_any ( pass_along );
2845 * If pass_along[3] is defined that this will upload just that track
2847 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
2849 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2850 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
2852 // May not actually get a track here as pass_along[3] can be null
2853 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2855 gboolean on_track = track ? TRUE : FALSE;
2857 if (on_track && !track->visible) {
2858 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
2862 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
2863 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2864 GTK_DIALOG_DESTROY_WITH_PARENT,
2866 GTK_RESPONSE_ACCEPT,
2868 GTK_RESPONSE_REJECT,
2871 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2872 GtkWidget *response_w = NULL;
2873 #if GTK_CHECK_VERSION (2, 20, 0)
2874 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2878 gtk_widget_grab_focus ( response_w );
2880 gpointer dgs = datasource_gps_setup ( dialog, on_track );
2882 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2883 datasource_gps_clean_up ( dgs );
2884 gtk_widget_destroy ( dialog );
2888 // Get info from reused datasource dialog widgets
2889 gchar* protocol = datasource_gps_get_protocol ( dgs );
2890 gchar* port = datasource_gps_get_descriptor ( dgs );
2891 // NB don't free the above strings as they're references to values held elsewhere
2892 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
2893 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
2894 gboolean turn_off = datasource_gps_get_off ( dgs );
2896 gtk_widget_destroy ( dialog );
2898 // When called from the viewport - work the corresponding layerspanel:
2900 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
2903 // Apply settings to transfer to the GPS device
2910 vik_layers_panel_get_viewport (vlp),
2918 * Acquire into this TRW Layer from any GPS Babel supported file
2920 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
2922 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2923 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2924 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2925 VikViewport *vvp = vik_window_viewport(vw);
2927 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
2930 static void trw_layer_new_wp ( gpointer lav[2] )
2932 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2933 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2934 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2935 instead return true if you want to update. */
2936 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 )
2937 vik_layers_panel_emit_update ( vlp );
2940 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2942 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2943 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2945 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2946 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2947 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2948 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2949 vik_layers_panel_emit_update ( vlp );
2953 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2955 /* NB do not care if wp is visible or not */
2956 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2959 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2961 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2962 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2964 /* Only 1 waypoint - jump straight to it */
2965 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2966 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2967 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2969 /* If at least 2 waypoints - find center and then zoom to fit */
2970 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2972 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2973 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2974 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2977 vik_layers_panel_emit_update ( vlp );
2980 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2982 static gpointer pass_along[2];
2984 GtkWidget *export_submenu;
2985 pass_along[0] = vtl;
2986 pass_along[1] = vlp;
2988 item = gtk_menu_item_new();
2989 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2990 gtk_widget_show ( item );
2992 /* Now with icons */
2993 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2994 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2996 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2997 gtk_widget_show ( item );
2999 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
3000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3001 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3002 gtk_widget_show ( item );
3004 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
3005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3006 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3007 gtk_widget_show ( item );
3009 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3010 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3011 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3012 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3013 gtk_widget_show ( item );
3015 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3016 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3017 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3018 gtk_widget_show ( item );
3020 export_submenu = gtk_menu_new ();
3021 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3022 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3023 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3024 gtk_widget_show ( item );
3025 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3027 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3029 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3030 gtk_widget_show ( item );
3032 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3034 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3035 gtk_widget_show ( item );
3037 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3039 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3040 gtk_widget_show ( item );
3042 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3044 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3045 gtk_widget_show ( item );
3047 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3048 item = gtk_menu_item_new_with_mnemonic ( external1 );
3049 g_free ( external1 );
3050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3051 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3052 gtk_widget_show ( item );
3054 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3055 item = gtk_menu_item_new_with_mnemonic ( external2 );
3056 g_free ( external2 );
3057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3058 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3059 gtk_widget_show ( item );
3061 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3062 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3063 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3064 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3065 gtk_widget_show ( item );
3067 #ifdef VIK_CONFIG_GEONAMES
3068 GtkWidget *wikipedia_submenu = gtk_menu_new();
3069 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3070 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3071 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3072 gtk_widget_show(item);
3073 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3075 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3076 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3078 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3079 gtk_widget_show ( item );
3081 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3082 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3083 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3084 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3085 gtk_widget_show ( item );
3088 #ifdef VIK_CONFIG_GEOTAG
3089 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3090 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3091 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3092 gtk_widget_show ( item );
3095 GtkWidget *acquire_submenu = gtk_menu_new ();
3096 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3097 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3098 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3099 gtk_widget_show ( item );
3100 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3102 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3104 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3105 gtk_widget_show ( item );
3107 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
3108 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3110 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3111 gtk_widget_show ( item );
3114 #ifdef VIK_CONFIG_OPENSTREETMAP
3115 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3116 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3117 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3118 gtk_widget_show ( item );
3121 #ifdef VIK_CONFIG_GEOCACHES
3122 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3124 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3125 gtk_widget_show ( item );
3128 #ifdef VIK_CONFIG_GEOTAG
3129 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3131 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3132 gtk_widget_show ( item );
3135 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3137 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3138 gtk_widget_show ( item );
3140 GtkWidget *upload_submenu = gtk_menu_new ();
3141 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3142 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3143 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3144 gtk_widget_show ( item );
3145 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3147 #ifdef VIK_CONFIG_OPENSTREETMAP
3148 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3151 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3152 gtk_widget_show ( item );
3155 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3156 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3157 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3158 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3159 gtk_widget_show ( item );
3161 GtkWidget *delete_submenu = gtk_menu_new ();
3162 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3164 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3165 gtk_widget_show ( item );
3166 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3168 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3171 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3172 gtk_widget_show ( item );
3174 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3177 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3178 gtk_widget_show ( item );
3180 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3183 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3184 gtk_widget_show ( item );
3186 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3187 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3189 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3190 gtk_widget_show ( item );
3192 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3193 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3195 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3196 gtk_widget_show ( item );
3199 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3200 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3202 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3203 gtk_widget_show ( item );
3207 // Fake Waypoint UUIDs vi simple increasing integer
3208 static guint wp_uuid = 0;
3210 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3214 vik_waypoint_set_name (wp, name);
3216 if ( VIK_LAYER(vtl)->realized )
3218 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3220 // Visibility column always needed for waypoints
3221 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3222 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, NULL, TRUE, TRUE );
3224 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
3226 // Actual setting of visibility dependent on the waypoint
3227 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3229 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3232 highest_wp_number_add_wp(vtl, name);
3233 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3237 // Fake Track UUIDs vi simple increasing integer
3238 static guint tr_uuid = 0;
3240 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3244 vik_track_set_name (t, name);
3246 if ( VIK_LAYER(vtl)->realized )
3248 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3249 // Visibility column always needed for tracks
3250 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3251 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 );
3253 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 );
3255 // Actual setting of visibility dependent on the track
3256 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3258 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3261 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3265 /* to be called whenever a track has been deleted or may have been changed. */
3266 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3268 if (vtl->current_tp_track == trk )
3269 trw_layer_cancel_current_tp ( vtl, FALSE );
3272 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3275 gchar *newname = g_strdup(name);
3276 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
3277 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
3278 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3280 newname = new_newname;
3286 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3288 // No more uniqueness of name forced when loading from a file
3289 // This now makes this function a little redunant as we just flow the parameters through
3290 vik_trw_layer_add_waypoint ( vtl, name, wp );
3293 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3295 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3296 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3297 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3298 vik_track_free ( tr );
3299 vtl->route_finder_append = FALSE; /* this means we have added it */
3302 // No more uniqueness of name forced when loading from a file
3303 vik_trw_layer_add_track ( vtl, name, tr );
3305 if ( vtl->route_finder_check_added_track ) {
3306 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3307 vtl->route_finder_added_track = tr;
3312 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3314 *l = g_list_append(*l, id);
3318 * Move an item from one TRW layer to another TRW layer
3320 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3322 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3323 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3325 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3327 VikTrack *trk2 = vik_track_copy ( trk );
3328 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3329 vik_trw_layer_delete_track ( vtl_src, trk );
3332 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3333 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3335 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3337 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3338 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3339 trw_layer_delete_waypoint ( vtl_src, wp );
3343 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3345 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3346 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3348 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3349 GList *items = NULL;
3352 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3353 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3355 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3356 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3361 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3362 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3364 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3371 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3372 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3377 VikTrack *trk; // input
3378 gpointer uuid; // output
3381 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3383 trku_udata *user_data = udata;
3384 if ( trk == user_data->trk ) {
3385 user_data->uuid = id;
3391 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3393 gboolean was_visible = FALSE;
3395 if ( trk && trk->name ) {
3397 if ( trk == vtl->current_track ) {
3398 vtl->current_track = NULL;
3399 vtl->current_tp_track = NULL;
3400 vtl->current_tp_id = NULL;
3401 vtl->moving_tp = FALSE;
3404 was_visible = trk->visible;
3406 if ( trk == vtl->route_finder_current_track )
3407 vtl->route_finder_current_track = NULL;
3409 if ( trk == vtl->route_finder_added_track )
3410 vtl->route_finder_added_track = NULL;
3416 // Hmmm, want key of it
3417 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3419 if ( trkf && udata.uuid ) {
3420 /* could be current_tp, so we have to check */
3421 trw_layer_cancel_tps_of_track ( vtl, trk );
3423 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3426 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3427 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3428 g_hash_table_remove ( vtl->tracks, udata.uuid );
3435 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3437 gboolean was_visible = FALSE;
3439 if ( wp && wp->name ) {
3441 if ( wp == vtl->current_wp ) {
3442 vtl->current_wp = NULL;
3443 vtl->current_wp_id = NULL;
3444 vtl->moving_wp = FALSE;
3447 was_visible = wp->visible;
3453 // Hmmm, want key of it
3454 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3456 if ( wpf && udata.uuid ) {
3457 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3460 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3461 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3463 highest_wp_number_remove_wp(vtl, wp->name);
3464 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3473 // Only for temporary use by trw_layer_delete_waypoint_by_name
3474 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3476 wpu_udata *user_data = udata;
3477 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3478 user_data->uuid = id;
3485 * Delete a waypoint by the given name
3486 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3487 * as there be multiple waypoints with the same name
3489 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3492 // Fake a waypoint with the given name
3493 udata.wp = vik_waypoint_new ();
3494 vik_waypoint_set_name (udata.wp, name);
3495 // Currently only the name is used in this waypoint find function
3498 // Hmmm, want key of it
3499 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3501 vik_waypoint_free (udata.wp);
3503 if ( wpf && udata.uuid )
3504 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3510 VikTrack *trk; // input
3511 gpointer uuid; // output
3514 // Only for temporary use by trw_layer_delete_track_by_name
3515 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3517 tpu_udata *user_data = udata;
3518 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3519 user_data->uuid = id;
3526 * Delete a track by the given name
3527 * NOTE: ATM this will delete the first encountered Track with the specified name
3528 * as there be multiple track with the same name
3530 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3533 // Fake a track with the given name
3534 udata.trk = vik_track_new ();
3535 vik_track_set_name (udata.trk, name);
3536 // Currently only the name is used in this waypoint find function
3539 // Hmmm, want key of it
3540 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3542 vik_track_free (udata.trk);
3544 if ( trkf && udata.uuid )
3545 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3550 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3552 vik_treeview_item_delete (vt, it );
3555 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3558 vtl->current_track = NULL;
3559 vtl->route_finder_current_track = NULL;
3560 vtl->route_finder_added_track = NULL;
3561 if (vtl->current_tp_track)
3562 trw_layer_cancel_current_tp(vtl, FALSE);
3564 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3565 g_hash_table_remove_all(vtl->tracks_iters);
3566 g_hash_table_remove_all(vtl->tracks);
3568 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3571 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3573 vtl->current_wp = NULL;
3574 vtl->current_wp_id = NULL;
3575 vtl->moving_wp = FALSE;
3577 highest_wp_number_reset(vtl);
3579 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3580 g_hash_table_remove_all(vtl->waypoints_iters);
3581 g_hash_table_remove_all(vtl->waypoints);
3583 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3586 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3588 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3589 // Get confirmation from the user
3590 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3591 _("Are you sure you want to delete all tracks in %s?"),
3592 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3593 vik_trw_layer_delete_all_tracks (vtl);
3596 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3598 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3599 // Get confirmation from the user
3600 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3601 _("Are you sure you want to delete all waypoints in %s?"),
3602 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3603 vik_trw_layer_delete_all_waypoints (vtl);
3606 static void trw_layer_delete_item ( gpointer pass_along[6] )
3608 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3609 gboolean was_visible = FALSE;
3610 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3612 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3613 if ( wp && wp->name ) {
3614 if ( GPOINTER_TO_INT ( pass_along[4]) )
3615 // Get confirmation from the user
3616 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3617 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3618 _("Are you sure you want to delete the waypoint \"%s\""),
3621 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3626 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3627 if ( trk && trk->name ) {
3628 if ( GPOINTER_TO_INT ( pass_along[4]) )
3629 // Get confirmation from the user
3630 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3631 _("Are you sure you want to delete the track \"%s\""),
3634 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3638 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3642 static void trw_layer_properties_item ( gpointer pass_along[7] )
3644 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3645 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3647 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3649 if ( wp && wp->name )
3651 gboolean updated = FALSE;
3652 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3654 if ( updated && VIK_LAYER(vtl)->visible )
3655 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3660 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3661 if ( tr && tr->name )
3663 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3665 pass_along[1], /* vlp */
3666 pass_along[5] ); /* vvp */
3672 Parameter 1 -> VikLayersPanel
3673 Parameter 2 -> VikLayer
3674 Parameter 3 -> VikViewport
3676 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3679 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3680 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3683 /* since vlp not set, vl & vvp should be valid instead! */
3685 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3686 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3691 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3693 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3694 if ( trps && trps->data )
3695 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3698 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3700 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3701 if ( track && track->trackpoints )
3703 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3705 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
3706 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3707 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3708 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3709 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3713 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3715 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3716 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3718 vtl->current_track = track;
3719 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3721 if ( track->trackpoints )
3722 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3725 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
3727 * extend a track using route finder
3729 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3731 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3732 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3733 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3735 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3736 vtl->route_finder_coord = last_coord;
3737 vtl->route_finder_current_track = track;
3738 vtl->route_finder_started = TRUE;
3740 if ( track->trackpoints )
3741 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3746 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3748 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3749 /* Also warn if overwrite old elevation data */
3750 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3752 vik_track_apply_dem_data ( track );
3755 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3757 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3760 trps = g_list_last(trps);
3761 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3764 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3766 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3769 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3772 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3774 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3777 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3780 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3782 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3785 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3789 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3791 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3793 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3794 if ( trk && trk->trackpoints )
3796 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3797 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
3798 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3799 if ( pass_along[1] )
3800 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3802 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3806 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3808 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3809 trw_layer_tpwin_init ( vtl );
3812 /*************************************
3813 * merge/split by time routines
3814 *************************************/
3816 /* called for each key in track hash table.
3817 * If the current track has the same time stamp type, add it to the result,
3818 * except the one pointed by "exclude".
3819 * set exclude to NULL if there is no exclude to check.
3820 * Note that the result is in reverse (for performance reasons).
3825 gboolean with_timestamps;
3827 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3829 twt_udata *user_data = udata;
3830 VikTrackpoint *p1, *p2;
3832 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3836 if (VIK_TRACK(value)->trackpoints) {
3837 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3838 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3840 if ( user_data->with_timestamps ) {
3841 if (!p1->has_timestamp || !p2->has_timestamp) {
3846 // Don't add tracks with timestamps when getting non timestamp tracks
3847 if (p1->has_timestamp || p2->has_timestamp) {
3853 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3856 /* called for each key in track hash table. if original track user_data[1] is close enough
3857 * to the passed one, add it to list in user_data[0]
3859 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
3862 VikTrackpoint *p1, *p2;
3863 VikTrack *trk = VIK_TRACK(value);
3865 GList **nearby_tracks = ((gpointer *)user_data)[0];
3866 GList *tpoints = ((gpointer *)user_data)[1];
3869 * detect reasons for not merging, and return
3870 * if no reason is found not to merge, then do it.
3873 // Exclude the original track from the compiled list
3874 if (trk->trackpoints == tpoints) {
3878 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
3879 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
3881 if (trk->trackpoints) {
3882 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
3883 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
3885 if (!p1->has_timestamp || !p2->has_timestamp) {
3886 //g_print("no timestamp\n");
3890 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3891 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
3892 if (! (abs(t1 - p2->timestamp) < threshold ||
3894 abs(p1->timestamp - t2) < threshold)
3901 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
3904 /* comparison function used to sort tracks; a and b are hash table keys */
3905 /* Not actively used - can be restored if needed
3906 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3908 GHashTable *tracks = user_data;
3911 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3912 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3914 if (t1 < t2) return -1;
3915 if (t1 > t2) return 1;
3920 /* comparison function used to sort trackpoints */
3921 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3923 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3925 if (t1 < t2) return -1;
3926 if (t1 > t2) return 1;
3931 * comparison function which can be used to sort tracks or waypoints by name
3933 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3935 const gchar* namea = (const gchar*) a;
3936 const gchar* nameb = (const gchar*) b;
3937 if ( namea == NULL || nameb == NULL)
3940 // Same sort method as used in the vik_treeview_*_alphabetize functions
3941 return strcmp ( namea, nameb );
3945 * Attempt to merge selected track with other tracks specified by the user
3946 * Tracks to merge with must be of the same 'type' as the selected track -
3947 * either all with timestamps, or all without timestamps
3949 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3951 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3952 GList *other_tracks = NULL;
3953 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3955 if ( !track->trackpoints )
3959 udata.result = &other_tracks;
3960 udata.exclude = track->trackpoints;
3961 // Allow merging with 'similar' time type time tracks
3962 // i.e. either those times, or those without
3963 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3965 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3966 other_tracks = g_list_reverse(other_tracks);
3968 if ( !other_tracks ) {
3969 if ( udata.with_timestamps )
3970 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3972 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3976 // Sort alphabetically for user presentation
3977 // Convert into list of names for usage with dialog function
3978 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3979 GList *other_tracks_names = NULL;
3980 GList *iter = g_list_first ( other_tracks );
3982 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
3983 iter = g_list_next ( iter );
3986 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
3988 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3989 other_tracks_names, TRUE,
3990 _("Merge with..."), _("Select track to merge with"));
3991 g_list_free(other_tracks);
3992 g_list_free(other_tracks_names);
3997 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3998 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
4000 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4001 merge_track->trackpoints = NULL;
4002 vik_trw_layer_delete_track (vtl, merge_track);
4003 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4006 /* TODO: free data before free merge_list */
4007 for (l = merge_list; l != NULL; l = g_list_next(l))
4009 g_list_free(merge_list);
4010 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4014 // c.f. trw_layer_sorted_track_id_by_name_list
4015 // but don't add the specified track to the list (normally current track)
4016 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4018 twt_udata *user_data = udata;
4021 if (trk->trackpoints == user_data->exclude) {
4025 // Sort named list alphabetically
4026 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4030 * Join - this allows combining 'routes' and 'tracks'
4031 * i.e. doesn't care about whether tracks have consistent timestamps
4032 * ATM can only append one track at a time to the currently selected track
4034 static void trw_layer_append_track ( gpointer pass_along[6] )
4037 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4038 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4040 GList *other_tracks_names = NULL;
4042 // Sort alphabetically for user presentation
4043 // Convert into list of names for usage with dialog function
4044 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4046 udata.result = &other_tracks_names;
4047 udata.exclude = trk->trackpoints;
4049 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4051 // Note the limit to selecting one track only
4052 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4053 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4054 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4058 _("Select the track to append after the current track"));
4060 g_list_free(other_tracks_names);
4062 // It's a list, but shouldn't contain more than one other track!
4063 if ( append_list ) {
4065 for (l = append_list; l != NULL; l = g_list_next(l)) {
4066 // TODO: at present this uses the first track found by name,
4067 // which with potential multiple same named tracks may not be the one selected...
4068 VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data );
4069 if ( append_track ) {
4070 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4071 append_track->trackpoints = NULL;
4072 vik_trw_layer_delete_track (vtl, append_track);
4075 for (l = append_list; l != NULL; l = g_list_next(l))
4077 g_list_free(append_list);
4078 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4082 /* merge by segments */
4083 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4085 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4086 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4087 guint segments = vik_track_merge_segments ( trk );
4088 // NB currently no need to redraw as segments not actually shown on the display
4089 // However inform the user of what happened:
4091 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4092 g_snprintf(str, 64, tmp_str, segments);
4093 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4096 /* merge by time routine */
4097 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4099 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4103 GList *tracks_with_timestamp = NULL;
4104 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4105 if (orig_trk->trackpoints &&
4106 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4107 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4112 udata.result = &tracks_with_timestamp;
4113 udata.exclude = orig_trk->trackpoints;
4114 udata.with_timestamps = TRUE;
4115 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4116 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4118 if (!tracks_with_timestamp) {
4119 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4122 g_list_free(tracks_with_timestamp);
4124 static guint threshold_in_minutes = 1;
4125 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4126 _("Merge Threshold..."),
4127 _("Merge when time between tracks less than:"),
4128 &threshold_in_minutes)) {
4132 // keep attempting to merge all tracks until no merges within the time specified is possible
4133 gboolean attempt_merge = TRUE;
4134 GList *nearby_tracks = NULL;
4136 static gpointer params[3];
4138 while ( attempt_merge ) {
4140 // Don't try again unless tracks have changed
4141 attempt_merge = FALSE;
4143 trps = orig_trk->trackpoints;
4147 if (nearby_tracks) {
4148 g_list_free(nearby_tracks);
4149 nearby_tracks = NULL;
4152 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4153 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4155 /* g_print("Original track times: %d and %d\n", t1, t2); */
4156 params[0] = &nearby_tracks;
4157 params[1] = (gpointer)trps;
4158 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4160 /* get a list of adjacent-in-time tracks */
4161 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4164 GList *l = nearby_tracks;
4167 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4168 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4170 t1 = get_first_trackpoint(l)->timestamp;
4171 t2 = get_last_trackpoint(l)->timestamp;
4172 #undef get_first_trackpoint
4173 #undef get_last_trackpoint
4174 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4177 /* remove trackpoints from merged track, delete track */
4178 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4179 VIK_TRACK(l->data)->trackpoints = NULL;
4180 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4182 // Tracks have changed, therefore retry again against all the remaining tracks
4183 attempt_merge = TRUE;
4188 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
4191 g_list_free(nearby_tracks);
4192 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4196 * Split a track at the currently selected trackpoint
4198 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl )
4200 if ( !vtl->current_tpl )
4203 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
4204 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4206 VikTrack *tr = vik_track_new ();
4207 GList *newglist = g_list_alloc ();
4208 newglist->prev = NULL;
4209 newglist->next = vtl->current_tpl->next;
4210 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4211 tr->trackpoints = newglist;
4213 vtl->current_tpl->next->prev = newglist; /* end old track here */
4214 vtl->current_tpl->next = NULL;
4216 vtl->current_tpl = newglist; /* change tp to first of new track. */
4217 vtl->current_tp_track = tr;
4221 vik_trw_layer_add_track ( vtl, name, tr );
4227 // Also need id of newly created track
4228 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4229 if ( trkf && udata.uuid )
4230 vtl->current_tp_id = udata.uuid;
4232 vtl->current_tp_id = NULL;
4234 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4239 /* split by time routine */
4240 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
4242 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4243 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4244 GList *trps = track->trackpoints;
4246 GList *newlists = NULL;
4247 GList *newtps = NULL;
4248 static guint thr = 1;
4255 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4256 _("Split Threshold..."),
4257 _("Split when time between trackpoints exceeds:"),
4262 /* iterate through trackpoints, and copy them into new lists without touching original list */
4263 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
4267 ts = VIK_TRACKPOINT(iter->data)->timestamp;
4269 g_print("panic: ts < prev_ts: this should never happen!\n");
4272 if (ts - prev_ts > thr*60) {
4273 /* flush accumulated trackpoints into new list */
4274 newlists = g_list_append(newlists, g_list_reverse(newtps));
4278 /* accumulate trackpoint copies in newtps, in reverse order */
4279 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4281 iter = g_list_next(iter);
4284 newlists = g_list_append(newlists, g_list_reverse(newtps));
4287 /* put lists of trackpoints into tracks */
4289 // Only bother updating if the split results in new tracks
4290 if (g_list_length (newlists) > 1) {
4295 tr = vik_track_new();
4296 tr->visible = track->visible;
4297 tr->trackpoints = (GList *)(iter->data);
4299 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4300 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4301 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
4302 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
4304 iter = g_list_next(iter);
4306 // Remove original track and then update the display
4307 vik_trw_layer_delete_track (vtl, track);
4308 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4310 g_list_free(newlists);
4314 * Split a track by the number of points as specified by the user
4316 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
4318 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4319 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4321 // Check valid track
4322 GList *trps = track->trackpoints;
4326 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4327 _("Split Every Nth Point"),
4328 _("Split on every Nth point:"),
4329 250, // Default value as per typical limited track capacity of various GPS devices
4333 // Was a valid number returned?
4339 GList *newlists = NULL;
4340 GList *newtps = NULL;
4345 /* accumulate trackpoint copies in newtps, in reverse order */
4346 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4348 if (count >= points) {
4349 /* flush accumulated trackpoints into new list */
4350 newlists = g_list_append(newlists, g_list_reverse(newtps));
4354 iter = g_list_next(iter);
4357 // If there is a remaining chunk put that into the new split list
4358 // This may well be the whole track if no split points were encountered
4360 newlists = g_list_append(newlists, g_list_reverse(newtps));
4363 /* put lists of trackpoints into tracks */
4365 // Only bother updating if the split results in new tracks
4366 if (g_list_length (newlists) > 1) {
4371 tr = vik_track_new();
4372 tr->visible = track->visible;
4373 tr->trackpoints = (GList *)(iter->data);
4375 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4376 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4378 iter = g_list_next(iter);
4380 // Remove original track and then update the display
4381 vik_trw_layer_delete_track (vtl, track);
4382 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4384 g_list_free(newlists);
4388 * Split a track at the currently selected trackpoint
4390 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
4392 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4393 trw_layer_split_at_selected_trackpoint ( vtl );
4397 * Split a track by its segments
4399 static void trw_layer_split_segments ( gpointer pass_along[6] )
4401 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4402 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4405 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
4408 for ( i = 0; i < ntracks; i++ ) {
4410 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
4411 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
4416 // Remove original track
4417 vik_trw_layer_delete_track ( vtl, trk );
4418 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4421 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
4424 /* end of split/merge routines */
4427 * Delete adjacent track points at the same position
4428 * AKA Delete Dulplicates on the Properties Window
4430 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
4432 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4433 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4435 gulong removed = vik_track_remove_dup_points ( trk );
4437 // Track has been updated so update tps:
4438 trw_layer_cancel_tps_of_track ( vtl, trk );
4440 // Inform user how much was deleted as it's not obvious from the normal view
4442 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4443 g_snprintf(str, 64, tmp_str, removed);
4444 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4446 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4450 * Delete adjacent track points with the same timestamp
4451 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
4453 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
4455 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4456 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4458 gulong removed = vik_track_remove_same_time_points ( trk );
4460 // Track has been updated so update tps:
4461 trw_layer_cancel_tps_of_track ( vtl, trk );
4463 // Inform user how much was deleted as it's not obvious from the normal view
4465 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4466 g_snprintf(str, 64, tmp_str, removed);
4467 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4469 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4475 static void trw_layer_reverse ( gpointer pass_along[6] )
4477 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4478 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4480 // Check valid track
4481 GList *trps = track->trackpoints;
4485 vik_track_reverse ( track );
4487 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4491 * Similar to trw_layer_enum_item, but this uses a sorted method
4494 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4496 GList **list = (GList**)udata;
4497 // *list = g_list_prepend(*all, key); //unsorted method
4498 // Sort named list alphabetically
4499 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4504 * Now Waypoint specific sort
4506 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4508 GList **list = (GList**)udata;
4509 // Sort named list alphabetically
4510 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4514 * Track specific sort
4516 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4518 GList **list = (GList**)udata;
4519 // Sort named list alphabetically
4520 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4525 gboolean has_same_track_name;
4526 const gchar *same_track_name;
4527 } same_track_name_udata;
4529 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4531 const gchar* namea = (const gchar*) aa;
4532 const gchar* nameb = (const gchar*) bb;
4535 gint result = strcmp ( namea, nameb );
4537 if ( result == 0 ) {
4538 // Found two names the same
4539 same_track_name_udata *user_data = udata;
4540 user_data->has_same_track_name = TRUE;
4541 user_data->same_track_name = namea;
4544 // Leave ordering the same
4549 * Find out if any tracks have the same name in this layer
4551 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4553 // Sort items by name, then compare if any next to each other are the same
4555 GList *track_names = NULL;
4556 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4559 if ( ! track_names )
4562 same_track_name_udata udata;
4563 udata.has_same_track_name = FALSE;
4565 // Use sort routine to traverse list comparing items
4566 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4567 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4568 // Still no tracks...
4572 return udata.has_same_track_name;
4576 * Force unqiue track names for this layer
4577 * Note the panel is a required parameter to enable the update of the names displayed
4579 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4581 // . Search list for an instance of repeated name
4582 // . get track of this name
4583 // . create new name
4584 // . rename track & update equiv. treeview iter
4585 // . repeat until all different
4587 same_track_name_udata udata;
4589 GList *track_names = NULL;
4590 udata.has_same_track_name = FALSE;
4591 udata.same_track_name = NULL;
4593 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4596 if ( ! track_names )
4599 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4601 // Still no tracks...
4602 if ( ! dummy_list1 )
4605 while ( udata.has_same_track_name ) {
4607 // Find a track with the same name
4608 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4612 g_critical("Houston, we've had a problem.");
4613 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4614 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4619 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4620 vik_track_set_name ( trk, newname );
4626 // Need want key of it for treeview update
4627 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4629 if ( trkf && udataU.uuid ) {
4631 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4634 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4635 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4636 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4641 // Start trying to find same names again...
4643 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4644 udata.has_same_track_name = FALSE;
4645 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4647 // No tracks any more - give up searching
4648 if ( ! dummy_list2 )
4649 udata.has_same_track_name = FALSE;
4653 vik_layers_panel_emit_update ( vlp );
4659 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4661 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4664 // Ensure list of track names offered is unique
4665 if ( trw_layer_has_same_track_names ( vtl ) ) {
4666 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4667 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4668 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4674 // Sort list alphabetically for better presentation
4675 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4678 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4682 // Get list of items to delete from the user
4683 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4686 _("Delete Selection"),
4687 _("Select tracks to delete"));
4690 // Delete requested tracks
4691 // since specificly requested, IMHO no need for extra confirmation
4692 if ( delete_list ) {
4694 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4695 // This deletes first trk it finds of that name (but uniqueness is enforced above)
4696 trw_layer_delete_track_by_name (vtl, l->data);
4698 g_list_free(delete_list);
4699 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4704 gboolean has_same_waypoint_name;
4705 const gchar *same_waypoint_name;
4706 } same_waypoint_name_udata;
4708 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4710 const gchar* namea = (const gchar*) aa;
4711 const gchar* nameb = (const gchar*) bb;
4714 gint result = strcmp ( namea, nameb );
4716 if ( result == 0 ) {
4717 // Found two names the same
4718 same_waypoint_name_udata *user_data = udata;
4719 user_data->has_same_waypoint_name = TRUE;
4720 user_data->same_waypoint_name = namea;
4723 // Leave ordering the same
4728 * Find out if any waypoints have the same name in this layer
4730 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
4732 // Sort items by name, then compare if any next to each other are the same
4734 GList *waypoint_names = NULL;
4735 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4738 if ( ! waypoint_names )
4741 same_waypoint_name_udata udata;
4742 udata.has_same_waypoint_name = FALSE;
4744 // Use sort routine to traverse list comparing items
4745 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4746 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4747 // Still no waypoints...
4751 return udata.has_same_waypoint_name;
4755 * Force unqiue waypoint names for this layer
4756 * Note the panel is a required parameter to enable the update of the names displayed
4758 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4760 // . Search list for an instance of repeated name
4761 // . get waypoint of this name
4762 // . create new name
4763 // . rename waypoint & update equiv. treeview iter
4764 // . repeat until all different
4766 same_waypoint_name_udata udata;
4768 GList *waypoint_names = NULL;
4769 udata.has_same_waypoint_name = FALSE;
4770 udata.same_waypoint_name = NULL;
4772 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4775 if ( ! waypoint_names )
4778 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4780 // Still no waypoints...
4781 if ( ! dummy_list1 )
4784 while ( udata.has_same_waypoint_name ) {
4786 // Find a waypoint with the same name
4787 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
4791 g_critical("Houston, we've had a problem.");
4792 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4793 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
4798 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
4799 vik_waypoint_set_name ( waypoint, newname );
4802 udataU.wp = waypoint;
4805 // Need want key of it for treeview update
4806 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4808 if ( wpf && udataU.uuid ) {
4810 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4813 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4814 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4815 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4820 // Start trying to find same names again...
4821 waypoint_names = NULL;
4822 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4823 udata.has_same_waypoint_name = FALSE;
4824 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4826 // No waypoints any more - give up searching
4827 if ( ! dummy_list2 )
4828 udata.has_same_waypoint_name = FALSE;
4832 vik_layers_panel_emit_update ( vlp );
4838 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4840 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4843 // Ensure list of waypoint names offered is unique
4844 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
4845 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4846 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4847 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4853 // Sort list alphabetically for better presentation
4854 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4856 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4860 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4862 // Get list of items to delete from the user
4863 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4866 _("Delete Selection"),
4867 _("Select waypoints to delete"));
4870 // Delete requested waypoints
4871 // since specificly requested, IMHO no need for extra confirmation
4872 if ( delete_list ) {
4874 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4875 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
4876 trw_layer_delete_waypoint_by_name (vtl, l->data);
4878 g_list_free(delete_list);
4879 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4884 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4886 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4888 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4891 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4893 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4894 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4898 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4900 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4902 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
4904 // No actual change to the name supplied
4905 if (strcmp(newname, wp->name) == 0 )
4908 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4911 // An existing waypoint has been found with the requested name
4912 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4913 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
4918 // Update WP name and refresh the treeview
4919 vik_waypoint_set_name (wp, newname);
4921 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4922 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4925 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4930 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4932 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
4934 // No actual change to the name supplied
4935 if (strcmp(newname, trk->name) == 0)
4938 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4941 // An existing track has been found with the requested name
4942 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4943 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4947 // Update track name and refresh GUI parts
4948 vik_track_set_name (trk, newname);
4950 // Update any subwindows that could be displaying this track which has changed name
4951 // Only one Track Edit Window
4952 if ( l->current_tp_track == trk && l->tpwin ) {
4953 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
4955 // Property Dialog of the track
4956 vik_trw_layer_propwin_update ( trk );
4958 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4959 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4962 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4969 static gboolean is_valid_geocache_name ( gchar *str )
4971 gint len = strlen ( str );
4972 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]));
4975 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
4977 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4978 a_acquire_set_filter_track ( trk );
4981 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
4983 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
4984 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
4987 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
4989 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4991 gchar *escaped = uri_escape ( tr->comment );
4992 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4993 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4999 /* vlp can be NULL if necessary - i.e. right-click from a tool */
5000 /* viewpoint is now available instead */
5001 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
5003 static gpointer pass_along[8];
5005 gboolean rv = FALSE;
5008 pass_along[1] = vlp;
5009 pass_along[2] = GINT_TO_POINTER (subtype);
5010 pass_along[3] = sublayer;
5011 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5012 pass_along[5] = vvp;
5013 pass_along[6] = iter;
5014 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
5016 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5020 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5021 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5022 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5023 gtk_widget_show ( item );
5025 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5026 VikTrwLayer *vtl = l;
5027 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
5028 if (tr && tr->property_dialog)
5029 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5032 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5034 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5035 gtk_widget_show ( item );
5037 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
5038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
5039 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5040 gtk_widget_show ( item );
5042 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
5043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
5044 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5045 gtk_widget_show ( item );
5047 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5049 gboolean separator_created = FALSE;
5051 /* could be a right-click using the tool */
5052 if ( vlp != NULL ) {
5053 item = gtk_menu_item_new ();
5054 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5055 gtk_widget_show ( item );
5057 separator_created = TRUE;
5059 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
5062 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5063 gtk_widget_show ( item );
5066 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5068 if ( wp && wp->name ) {
5069 if ( is_valid_geocache_name ( wp->name ) ) {
5071 if ( !separator_created ) {
5072 item = gtk_menu_item_new ();
5073 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5074 gtk_widget_show ( item );
5075 separator_created = TRUE;
5078 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
5079 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
5080 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5081 gtk_widget_show ( item );
5085 if ( wp && wp->image )
5087 if ( !separator_created ) {
5088 item = gtk_menu_item_new ();
5089 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5090 gtk_widget_show ( item );
5091 separator_created = TRUE;
5094 // Set up image paramater
5095 pass_along[5] = wp->image;
5097 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
5098 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
5099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
5100 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5101 gtk_widget_show ( item );
5103 #ifdef VIK_CONFIG_GEOTAG
5104 GtkWidget *geotag_submenu = gtk_menu_new ();
5105 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
5106 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
5107 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5108 gtk_widget_show ( item );
5109 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
5111 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
5112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
5113 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5114 gtk_widget_show ( item );
5116 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
5117 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
5118 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5119 gtk_widget_show ( item );
5126 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
5129 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
5130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
5132 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5133 gtk_widget_show ( item );
5136 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
5138 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
5139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
5141 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5142 gtk_widget_show ( item );
5144 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
5145 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5146 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
5147 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5148 gtk_widget_show ( item );
5150 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
5151 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
5153 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5154 gtk_widget_show ( item );
5156 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
5157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
5159 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5160 gtk_widget_show ( item );
5163 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
5167 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
5168 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5169 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
5170 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5171 gtk_widget_show ( item );
5173 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
5174 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
5176 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5177 gtk_widget_show ( item );
5179 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
5180 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5181 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
5182 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5183 gtk_widget_show ( item );
5186 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5188 item = gtk_menu_item_new ();
5189 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5190 gtk_widget_show ( item );
5192 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
5193 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5194 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
5195 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5196 gtk_widget_show ( item );
5198 GtkWidget *goto_submenu;
5199 goto_submenu = gtk_menu_new ();
5200 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5201 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5202 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5203 gtk_widget_show ( item );
5204 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
5206 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
5207 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
5208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
5209 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5210 gtk_widget_show ( item );
5212 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
5213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
5215 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5216 gtk_widget_show ( item );
5218 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
5219 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
5220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
5221 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5222 gtk_widget_show ( item );
5224 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
5225 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
5226 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
5227 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5228 gtk_widget_show ( item );
5230 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
5231 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
5232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
5233 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5234 gtk_widget_show ( item );
5236 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
5237 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
5238 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
5239 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5240 gtk_widget_show ( item );
5242 GtkWidget *combine_submenu;
5243 combine_submenu = gtk_menu_new ();
5244 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
5245 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
5246 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5247 gtk_widget_show ( item );
5248 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
5250 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
5251 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
5252 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5253 gtk_widget_show ( item );
5255 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
5256 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
5257 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5258 gtk_widget_show ( item );
5260 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
5261 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
5262 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5263 gtk_widget_show ( item );
5265 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
5266 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
5267 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5268 gtk_widget_show ( item );
5270 GtkWidget *split_submenu;
5271 split_submenu = gtk_menu_new ();
5272 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
5273 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
5274 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5275 gtk_widget_show ( item );
5276 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
5278 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
5279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
5280 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5281 gtk_widget_show ( item );
5283 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
5284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
5285 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5286 gtk_widget_show ( item );
5288 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
5289 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
5290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
5291 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5292 gtk_widget_show ( item );
5294 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
5295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
5296 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5297 gtk_widget_show ( item );
5298 // Make it available only when a trackpoint is selected.
5299 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
5301 GtkWidget *delete_submenu;
5302 delete_submenu = gtk_menu_new ();
5303 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
5304 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
5305 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5306 gtk_widget_show ( item );
5307 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
5309 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
5310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
5311 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5312 gtk_widget_show ( item );
5314 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
5315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
5316 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5317 gtk_widget_show ( item );
5319 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
5320 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
5321 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
5322 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5323 gtk_widget_show ( item );
5325 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
5327 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
5328 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
5329 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
5330 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5331 gtk_widget_show ( item );
5334 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
5335 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
5336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
5337 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5338 gtk_widget_show ( item );
5340 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
5341 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
5342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
5343 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5344 gtk_widget_show ( item );
5346 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
5347 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
5348 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
5349 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5350 gtk_widget_show ( item );
5352 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
5353 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
5354 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
5355 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
5356 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5357 gtk_widget_show ( item );
5360 GtkWidget *upload_submenu = gtk_menu_new ();
5361 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
5362 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5363 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5364 gtk_widget_show ( item );
5365 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
5367 #ifdef VIK_CONFIG_OPENSTREETMAP
5368 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
5369 // Convert internal pointer into actual track for usage outside this file
5370 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
5371 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5372 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
5373 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5374 gtk_widget_show ( item );
5377 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
5378 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
5379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
5380 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5381 gtk_widget_show ( item );
5383 if ( is_valid_google_route ( l, sublayer ) )
5385 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
5386 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
5387 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
5388 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5389 gtk_widget_show ( item );
5392 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
5393 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
5395 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5396 gtk_widget_show ( item );
5398 /* ATM This function is only available via the layers panel, due to needing a vlp */
5400 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
5401 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
5402 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
5404 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5405 gtk_widget_show ( item );
5409 #ifdef VIK_CONFIG_GEOTAG
5410 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
5411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
5412 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5413 gtk_widget_show ( item );
5416 // Only show on viewport popmenu when a trackpoint is selected
5417 if ( ! vlp && l->current_tpl ) {
5419 item = gtk_menu_item_new ();
5420 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5421 gtk_widget_show ( item );
5423 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
5424 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
5425 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
5426 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5427 gtk_widget_show ( item );
5435 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
5438 if (!vtl->current_tpl)
5440 if (!vtl->current_tpl->next)
5443 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
5444 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
5446 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
5449 VikTrackpoint *tp_new = vik_trackpoint_new();
5450 struct LatLon ll_current, ll_next;
5451 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
5452 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
5454 /* main positional interpolation */
5455 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
5456 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
5458 /* Now other properties that can be interpolated */
5459 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
5461 if (tp_current->has_timestamp && tp_next->has_timestamp) {
5462 /* Note here the division is applied to each part, then added
5463 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
5464 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
5465 tp_new->has_timestamp = TRUE;
5468 if (tp_current->speed != NAN && tp_next->speed != NAN)
5469 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
5471 /* TODO - improve interpolation of course, as it may not be correct.
5472 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
5473 [similar applies if value is in radians] */
5474 if (tp_current->course != NAN && tp_next->course != NAN)
5475 tp_new->speed = (tp_current->course + tp_next->course)/2;
5477 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
5479 /* Insert new point into the trackpoints list after the current TP */
5480 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5481 gint index = g_list_index ( tr->trackpoints, tp_current );
5483 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
5488 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
5494 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
5498 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
5500 if ( vtl->current_tpl )
5502 vtl->current_tpl = NULL;
5503 vtl->current_tp_track = NULL;
5504 vtl->current_tp_id = NULL;
5505 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5509 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
5511 g_assert ( vtl->tpwin != NULL );
5512 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
5513 trw_layer_cancel_current_tp ( vtl, TRUE );
5515 if ( vtl->current_tpl == NULL )
5518 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
5520 trw_layer_split_at_selected_trackpoint ( vtl );
5521 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5523 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5525 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5531 // Find available adjacent trackpoint
5532 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5534 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5535 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5537 // Delete current trackpoint
5538 vik_trackpoint_free ( vtl->current_tpl->data );
5539 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5541 // Set to current to the available adjacent trackpoint
5542 vtl->current_tpl = new_tpl;
5544 // Reset dialog with the available adjacent trackpoint
5545 if ( vtl->current_tp_track )
5546 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
5548 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5552 // Delete current trackpoint
5553 vik_trackpoint_free ( vtl->current_tpl->data );
5554 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5555 trw_layer_cancel_current_tp ( vtl, FALSE );
5558 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5560 if ( vtl->current_tp_track )
5561 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
5562 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
5564 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5566 if ( vtl->current_tp_track )
5567 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
5568 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5570 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5572 trw_layer_insert_tp_after_current_tp ( vtl );
5573 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5575 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
5576 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5579 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5583 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5584 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5585 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5586 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5587 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5589 if ( vtl->current_tpl )
5590 if ( vtl->current_tp_track )
5591 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5592 /* set layer name and TP data */
5595 /***************************************************************************
5597 ***************************************************************************/
5599 /*** Utility data structures and functions ****/
5603 gint closest_x, closest_y;
5604 gpointer *closest_wp_id;
5605 VikWaypoint *closest_wp;
5611 gint closest_x, closest_y;
5612 gpointer closest_track_id;
5613 VikTrackpoint *closest_tp;
5618 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5624 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5626 // If waypoint has an image then use the image size to select
5628 gint slackx, slacky;
5629 slackx = wp->image_width / 2;
5630 slacky = wp->image_height / 2;
5632 if ( x <= params->x + slackx && x >= params->x - slackx
5633 && y <= params->y + slacky && y >= params->y - slacky ) {
5634 params->closest_wp_id = id;
5635 params->closest_wp = wp;
5636 params->closest_x = x;
5637 params->closest_y = y;
5640 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5641 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5642 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5644 params->closest_wp_id = id;
5645 params->closest_wp = wp;
5646 params->closest_x = x;
5647 params->closest_y = y;
5651 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5653 GList *tpl = t->trackpoints;
5662 tp = VIK_TRACKPOINT(tpl->data);
5664 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5666 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5667 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5668 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5670 params->closest_track_id = id;
5671 params->closest_tp = tp;
5672 params->closest_tpl = tpl;
5673 params->closest_x = x;
5674 params->closest_y = y;
5680 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5682 TPSearchParams params;
5686 params.closest_track_id = NULL;
5687 params.closest_tp = NULL;
5688 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5689 return params.closest_tp;
5692 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5694 WPSearchParams params;
5698 params.closest_wp = NULL;
5699 params.closest_wp_id = NULL;
5700 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5701 return params.closest_wp;
5705 // Some forward declarations
5706 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5707 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5708 static void marker_end_move ( tool_ed_t *t );
5711 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5715 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5717 // Here always allow snapping back to the original location
5718 // this is useful when one decides not to move the thing afterall
5719 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5722 if ( event->state & GDK_CONTROL_MASK )
5724 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5726 new_coord = tp->coord;
5730 if ( event->state & GDK_SHIFT_MASK )
5732 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5734 new_coord = wp->coord;
5738 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5740 marker_moveto ( t, x, y );
5747 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5749 if ( t->holding && event->button == 1 )
5752 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5755 if ( event->state & GDK_CONTROL_MASK )
5757 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5759 new_coord = tp->coord;
5763 if ( event->state & GDK_SHIFT_MASK )
5765 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5767 new_coord = wp->coord;
5770 marker_end_move ( t );
5772 // Determine if working on a waypoint or a trackpoint
5773 if ( t->is_waypoint )
5774 vtl->current_wp->coord = new_coord;
5776 if ( vtl->current_tpl ) {
5777 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5780 if ( vtl->current_tp_track )
5781 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5786 vtl->current_wp = NULL;
5787 vtl->current_wp_id = NULL;
5788 trw_layer_cancel_current_tp ( vtl, FALSE );
5790 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5797 Returns true if a waypoint or track is found near the requested event position for this particular layer
5798 The item found is automatically selected
5799 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5801 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5803 if ( event->button != 1 )
5806 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5809 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5812 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5814 if (vtl->waypoints_visible) {
5815 WPSearchParams wp_params;
5816 wp_params.vvp = vvp;
5817 wp_params.x = event->x;
5818 wp_params.y = event->y;
5819 wp_params.closest_wp_id = NULL;
5820 wp_params.closest_wp = NULL;
5822 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5824 if ( wp_params.closest_wp ) {
5827 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5829 // Too easy to move it so must be holding shift to start immediately moving it
5830 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5831 if ( event->state & GDK_SHIFT_MASK ||
5832 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5833 // Put into 'move buffer'
5834 // NB vvp & vw already set in tet
5835 tet->vtl = (gpointer)vtl;
5836 tet->is_waypoint = TRUE;
5838 marker_begin_move (tet, event->x, event->y);
5841 vtl->current_wp = wp_params.closest_wp;
5842 vtl->current_wp_id = wp_params.closest_wp_id;
5844 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5850 if (vtl->tracks_visible) {
5851 TPSearchParams tp_params;
5852 tp_params.vvp = vvp;
5853 tp_params.x = event->x;
5854 tp_params.y = event->y;
5855 tp_params.closest_track_id = NULL;
5856 tp_params.closest_tp = NULL;
5858 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5860 if ( tp_params.closest_tp ) {
5862 // Always select + highlight the track
5863 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5865 tet->is_waypoint = FALSE;
5867 // Select the Trackpoint
5868 // Can move it immediately when control held or it's the previously selected tp
5869 if ( event->state & GDK_CONTROL_MASK ||
5870 vtl->current_tpl == tp_params.closest_tpl ) {
5871 // Put into 'move buffer'
5872 // NB vvp & vw already set in tet
5873 tet->vtl = (gpointer)vtl;
5874 marker_begin_move (tet, event->x, event->y);
5877 vtl->current_tpl = tp_params.closest_tpl;
5878 vtl->current_tp_id = tp_params.closest_track_id;
5879 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5881 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5884 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5886 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5891 /* these aren't the droids you're looking for */
5892 vtl->current_wp = NULL;
5893 vtl->current_wp_id = NULL;
5894 trw_layer_cancel_current_tp ( vtl, FALSE );
5897 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5902 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5904 if ( event->button != 3 )
5907 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5910 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5913 /* Post menu for the currently selected item */
5915 /* See if a track is selected */
5916 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5917 if ( track && track->visible ) {
5919 if ( track->name ) {
5921 if ( vtl->track_right_click_menu )
5922 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5924 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5930 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5932 if ( trkf && udataU.uuid ) {
5934 GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5936 trw_layer_sublayer_add_menu_items ( vtl,
5937 vtl->track_right_click_menu,
5939 VIK_TRW_LAYER_SUBLAYER_TRACK,
5945 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5951 /* See if a waypoint is selected */
5952 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5953 if ( waypoint && waypoint->visible ) {
5954 if ( waypoint->name ) {
5956 if ( vtl->wp_right_click_menu )
5957 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
5959 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5962 udata.wp = waypoint;
5965 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
5967 if ( wpf && udata.uuid ) {
5968 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
5970 trw_layer_sublayer_add_menu_items ( vtl,
5971 vtl->wp_right_click_menu,
5973 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5978 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5987 /* background drawing hook, to be passed the viewport */
5988 static gboolean tool_sync_done = TRUE;
5990 static gboolean tool_sync(gpointer data)
5992 VikViewport *vvp = data;
5993 gdk_threads_enter();
5994 vik_viewport_sync(vvp);
5995 tool_sync_done = TRUE;
5996 gdk_threads_leave();
6000 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
6003 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
6004 gdk_gc_set_function ( t->gc, GDK_INVERT );
6005 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6006 vik_viewport_sync(t->vvp);
6011 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
6013 VikViewport *vvp = t->vvp;
6014 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6015 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6019 if (tool_sync_done) {
6020 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
6021 tool_sync_done = FALSE;
6025 static void marker_end_move ( tool_ed_t *t )
6027 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6028 g_object_unref ( t->gc );
6032 /*** Edit waypoint ****/
6034 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6036 tool_ed_t *t = g_new(tool_ed_t, 1);
6042 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6044 WPSearchParams params;
6045 tool_ed_t *t = data;
6046 VikViewport *vvp = t->vvp;
6048 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6055 if ( !vtl->vl.visible || !vtl->waypoints_visible )
6058 if ( vtl->current_wp && vtl->current_wp->visible )
6060 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
6062 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
6064 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
6065 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
6067 if ( event->button == 3 )
6068 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6070 marker_begin_move(t, event->x, event->y);
6077 params.x = event->x;
6078 params.y = event->y;
6079 params.closest_wp_id = NULL;
6080 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6081 params.closest_wp = NULL;
6082 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6083 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
6085 // how do we get here?
6086 marker_begin_move(t, event->x, event->y);
6087 g_critical("shouldn't be here");
6090 else if ( params.closest_wp )
6092 if ( event->button == 3 )
6093 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6095 vtl->waypoint_rightclick = FALSE;
6097 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
6099 vtl->current_wp = params.closest_wp;
6100 vtl->current_wp_id = params.closest_wp_id;
6102 /* could make it so don't update if old WP is off screen and new is null but oh well */
6103 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6107 vtl->current_wp = NULL;
6108 vtl->current_wp_id = NULL;
6109 vtl->waypoint_rightclick = FALSE;
6110 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6114 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6116 tool_ed_t *t = data;
6117 VikViewport *vvp = t->vvp;
6119 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6124 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6127 if ( event->state & GDK_CONTROL_MASK )
6129 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6131 new_coord = tp->coord;
6135 if ( event->state & GDK_SHIFT_MASK )
6137 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6138 if ( wp && wp != vtl->current_wp )
6139 new_coord = wp->coord;
6144 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6146 marker_moveto ( t, x, y );
6153 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6155 tool_ed_t *t = data;
6156 VikViewport *vvp = t->vvp;
6158 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6161 if ( t->holding && event->button == 1 )
6164 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6167 if ( event->state & GDK_CONTROL_MASK )
6169 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6171 new_coord = tp->coord;
6175 if ( event->state & GDK_SHIFT_MASK )
6177 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6178 if ( wp && wp != vtl->current_wp )
6179 new_coord = wp->coord;
6182 marker_end_move ( t );
6184 vtl->current_wp->coord = new_coord;
6185 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6188 /* PUT IN RIGHT PLACE!!! */
6189 if ( event->button == 3 && vtl->waypoint_rightclick )
6191 if ( vtl->wp_right_click_menu )
6192 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
6193 if ( vtl->current_wp ) {
6194 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
6195 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 );
6196 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6198 vtl->waypoint_rightclick = FALSE;
6203 /**** Begin track ***/
6204 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
6209 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6211 vtl->current_track = NULL;
6212 return tool_new_track_click ( vtl, event, vvp );
6215 /*** New track ****/
6217 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
6224 GdkDrawable *drawable;
6230 * Draw specified pixmap
6232 static gboolean draw_sync ( gpointer data )
6234 draw_sync_t *ds = (draw_sync_t*) data;
6235 // Sometimes don't want to draw
6236 // normally because another update has taken precedent such as panning the display
6237 // which means this pixmap is no longer valid
6238 if ( ds->vtl->draw_sync_do ) {
6239 gdk_threads_enter();
6240 gdk_draw_drawable (ds->drawable,
6243 0, 0, 0, 0, -1, -1);
6244 ds->vtl->draw_sync_done = TRUE;
6245 gdk_threads_leave();
6250 static const gchar* distance_string (gdouble distance)
6254 /* draw label with distance */
6255 vik_units_distance_t dist_units = a_vik_get_units_distance ();
6256 switch (dist_units) {
6257 case VIK_UNITS_DISTANCE_MILES:
6258 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
6259 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
6260 } else if (distance < 1609.4) {
6261 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
6263 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
6267 // VIK_UNITS_DISTANCE_KILOMETRES
6268 if (distance >= 1000 && distance < 100000) {
6269 g_sprintf(str, "%3.2f km", distance/1000.0);
6270 } else if (distance < 1000) {
6271 g_sprintf(str, "%d m", (int)distance);
6273 g_sprintf(str, "%d km", (int)distance/1000);
6277 return g_strdup (str);
6281 * Actually set the message in statusbar
6283 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
6285 // Only show elevation data when track has some elevation properties
6286 gchar str_gain_loss[64];
6287 str_gain_loss[0] = '\0';
6289 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
6290 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
6291 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
6293 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
6296 // Write with full gain/loss information
6297 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
6298 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
6303 * Figure out what information should be set in the statusbar and then write it
6305 static void update_statusbar ( VikTrwLayer *vtl )
6307 // Get elevation data
6308 gdouble elev_gain, elev_loss;
6309 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6311 /* Find out actual distance of current track */
6312 gdouble distance = vik_track_get_length (vtl->current_track);
6313 const gchar *str = distance_string (distance);
6315 statusbar_write (str, elev_gain, elev_loss, vtl);
6317 g_free ((gpointer)str);
6321 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
6323 /* if we haven't sync'ed yet, we don't have time to do more. */
6324 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
6325 GList *iter = g_list_last ( vtl->current_track->trackpoints );
6327 static GdkPixmap *pixmap = NULL;
6329 // Need to check in case window has been resized
6330 w1 = vik_viewport_get_width(vvp);
6331 h1 = vik_viewport_get_height(vvp);
6333 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6335 gdk_drawable_get_size (pixmap, &w2, &h2);
6336 if (w1 != w2 || h1 != h2) {
6337 g_object_unref ( G_OBJECT ( pixmap ) );
6338 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6341 // Reset to background
6342 gdk_draw_drawable (pixmap,
6343 vtl->current_track_newpoint_gc,
6344 vik_viewport_get_pixmap(vvp),
6345 0, 0, 0, 0, -1, -1);
6347 draw_sync_t *passalong;
6350 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
6352 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
6353 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
6354 // thus when we come to reset to the background it would include what we have already drawn!!
6355 gdk_draw_line ( pixmap,
6356 vtl->current_track_newpoint_gc,
6357 x1, y1, event->x, event->y );
6358 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
6360 /* Find out actual distance of current track */
6361 gdouble distance = vik_track_get_length (vtl->current_track);
6363 // Now add distance to where the pointer is //
6366 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
6367 vik_coord_to_latlon ( &coord, &ll );
6368 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
6370 // Get elevation data
6371 gdouble elev_gain, elev_loss;
6372 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6374 // Adjust elevation data (if available) for the current pointer position
6376 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
6377 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
6378 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
6379 // Adjust elevation of last track point
6380 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
6382 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
6385 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
6389 const gchar *str = distance_string (distance);
6391 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
6392 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
6394 pango_layout_set_text (pl, str, -1);
6396 pango_layout_get_pixel_size ( pl, &wd, &hd );
6399 // offset from cursor a bit depending on font size
6403 // Create a background block to make the text easier to read over the background map
6404 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
6405 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
6406 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
6408 g_object_unref ( G_OBJECT ( pl ) );
6409 g_object_unref ( G_OBJECT ( background_block_gc ) );
6411 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
6412 passalong->vtl = vtl;
6413 passalong->pixmap = pixmap;
6414 passalong->drawable = GTK_WIDGET(vvp)->window;
6415 passalong->gc = vtl->current_track_newpoint_gc;
6417 // Update statusbar with full gain/loss information
6418 statusbar_write (str, elev_gain, elev_loss, vtl);
6420 g_free ((gpointer)str);
6422 // draw pixmap when we have time to
6423 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
6424 vtl->draw_sync_done = FALSE;
6425 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
6427 return VIK_LAYER_TOOL_ACK;
6430 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
6432 if ( vtl->current_track && event->keyval == GDK_Escape ) {
6433 vtl->current_track = NULL;
6434 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6436 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
6438 if ( vtl->current_track->trackpoints )
6440 GList *last = g_list_last(vtl->current_track->trackpoints);
6441 g_free ( last->data );
6442 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6445 update_statusbar ( vtl );
6447 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6453 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6457 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6460 if ( event->button == 2 ) {
6461 // As the display is panning, the new track pixmap is now invalid so don't draw it
6462 // otherwise this drawing done results in flickering back to an old image
6463 vtl->draw_sync_do = FALSE;
6467 if ( event->button == 3 && vtl->current_track )
6470 if ( vtl->current_track->trackpoints )
6472 GList *last = g_list_last(vtl->current_track->trackpoints);
6473 g_free ( last->data );
6474 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6476 update_statusbar ( vtl );
6478 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6482 if ( event->type == GDK_2BUTTON_PRESS )
6484 /* subtract last (duplicate from double click) tp then end */
6485 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
6487 GList *last = g_list_last(vtl->current_track->trackpoints);
6488 g_free ( last->data );
6489 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6490 /* undo last, then end */
6491 vtl->current_track = NULL;
6493 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6497 if ( ! vtl->current_track )
6499 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
6500 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
6502 vtl->current_track = vik_track_new();
6503 vtl->current_track->visible = TRUE;
6504 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
6506 /* incase it was created by begin track */
6507 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
6512 tp = vik_trackpoint_new();
6513 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
6515 /* snap to other TP */
6516 if ( event->state & GDK_CONTROL_MASK )
6518 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6520 tp->coord = other_tp->coord;
6523 tp->newsegment = FALSE;
6524 tp->has_timestamp = FALSE;
6526 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
6527 /* Auto attempt to get elevation from DEM data (if it's available) */
6528 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
6530 vtl->ct_x1 = vtl->ct_x2;
6531 vtl->ct_y1 = vtl->ct_y2;
6532 vtl->ct_x2 = event->x;
6533 vtl->ct_y2 = event->y;
6535 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6539 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6541 if ( event->button == 2 ) {
6542 // Pan moving ended - enable potential point drawing again
6543 vtl->draw_sync_do = TRUE;
6544 vtl->draw_sync_done = TRUE;
6548 /*** New waypoint ****/
6550 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6555 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6558 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6560 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6561 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
6562 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6567 /*** Edit trackpoint ****/
6569 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6571 tool_ed_t *t = g_new(tool_ed_t, 1);
6577 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6579 tool_ed_t *t = data;
6580 VikViewport *vvp = t->vvp;
6581 TPSearchParams params;
6582 /* OUTDATED DOCUMENTATION:
6583 find 5 pixel range on each side. then put these UTM, and a pointer
6584 to the winning track name (and maybe the winning track itself), and a
6585 pointer to the winning trackpoint, inside an array or struct. pass
6586 this along, do a foreach on the tracks which will do a foreach on the
6589 params.x = event->x;
6590 params.y = event->y;
6591 params.closest_track_id = NULL;
6592 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6593 params.closest_tp = NULL;
6595 if ( event->button != 1 )
6598 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6601 if ( !vtl->vl.visible || !vtl->tracks_visible )
6604 if ( vtl->current_tpl )
6606 /* 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.) */
6607 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6608 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6613 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6615 if ( current_tr->visible &&
6616 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
6617 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6618 marker_begin_move ( t, event->x, event->y );
6624 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6626 if ( params.closest_tp )
6628 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
6629 vtl->current_tpl = params.closest_tpl;
6630 vtl->current_tp_id = params.closest_track_id;
6631 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
6632 trw_layer_tpwin_init ( vtl );
6633 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
6634 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6638 /* these aren't the droids you're looking for */
6642 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6644 tool_ed_t *t = data;
6645 VikViewport *vvp = t->vvp;
6647 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6653 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6656 if ( event->state & GDK_CONTROL_MASK )
6658 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6659 if ( tp && tp != vtl->current_tpl->data )
6660 new_coord = tp->coord;
6662 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6665 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6666 marker_moveto ( t, x, y );
6674 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6676 tool_ed_t *t = data;
6677 VikViewport *vvp = t->vvp;
6679 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6681 if ( event->button != 1)
6686 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6689 if ( event->state & GDK_CONTROL_MASK )
6691 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6692 if ( tp && tp != vtl->current_tpl->data )
6693 new_coord = tp->coord;
6696 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6698 marker_end_move ( t );
6700 /* diff dist is diff from orig */
6702 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6704 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6711 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
6712 /*** Route Finder ***/
6713 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6718 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6721 if ( !vtl ) return FALSE;
6722 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6723 if ( event->button == 3 && vtl->route_finder_current_track ) {
6725 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6727 vtl->route_finder_coord = *new_end;
6729 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6730 /* remove last ' to:...' */
6731 if ( vtl->route_finder_current_track->comment ) {
6732 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6733 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6734 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6735 last_to - vtl->route_finder_current_track->comment - 1);
6736 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6741 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6742 struct LatLon start, end;
6743 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6744 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6747 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6748 vik_coord_to_latlon ( &(tmp), &end );
6749 vtl->route_finder_coord = tmp; /* for continuations */
6751 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6752 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6753 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6755 vtl->route_finder_check_added_track = TRUE;
6756 vtl->route_finder_started = FALSE;
6759 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6760 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6761 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6762 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6763 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6764 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
6767 /* see if anything was done -- a track was added or appended to */
6768 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6769 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 ) );
6770 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6771 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6772 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6773 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6775 vtl->route_finder_added_track = NULL;
6776 vtl->route_finder_check_added_track = FALSE;
6777 vtl->route_finder_append = FALSE;
6779 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6781 vtl->route_finder_started = TRUE;
6782 vtl->route_finder_coord = tmp;
6783 vtl->route_finder_current_track = NULL;
6789 /*** Show picture ****/
6791 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6796 /* Params are: vvp, event, last match found or NULL */
6797 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6799 if ( wp->image && wp->visible )
6801 gint x, y, slackx, slacky;
6802 GdkEventButton *event = (GdkEventButton *) params[1];
6804 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6805 slackx = wp->image_width / 2;
6806 slacky = wp->image_height / 2;
6807 if ( x <= event->x + slackx && x >= event->x - slackx
6808 && y <= event->y + slacky && y >= event->y - slacky )
6810 params[2] = wp->image; /* we've found a match. however continue searching
6811 * since we want to find the last match -- that
6812 * is, the match that was drawn last. */
6817 static void trw_layer_show_picture ( gpointer pass_along[6] )
6819 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6821 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
6824 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6825 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6826 g_free ( quoted_file );
6827 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6829 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() );
6830 g_error_free ( err );
6833 #endif /* WINDOWS */
6836 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6838 gpointer params[3] = { vvp, event, NULL };
6839 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6841 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6844 static gpointer pass_along[6];
6845 pass_along[0] = vtl;
6846 pass_along[5] = params[2];
6847 trw_layer_show_picture ( pass_along );
6848 return TRUE; /* found a match */
6851 return FALSE; /* go through other layers, searching for a match */
6854 /***************************************************************************
6856 ***************************************************************************/
6862 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6864 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6865 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6868 /* Structure for thumbnail creating data used in the background thread */
6870 VikTrwLayer *vtl; // Layer needed for redrawing
6871 GSList *pics; // Image list
6872 } thumbnail_create_thread_data;
6874 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6876 guint total = g_slist_length(tctd->pics), done = 0;
6877 while ( tctd->pics )
6879 a_thumbnails_create ( (gchar *) tctd->pics->data );
6880 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6882 return -1; /* Abort thread */
6884 tctd->pics = tctd->pics->next;
6887 // Redraw to show the thumbnails as they are now created
6888 if ( IS_VIK_LAYER(tctd->vtl) )
6889 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6894 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6896 while ( tctd->pics )
6898 g_free ( tctd->pics->data );
6899 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6904 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6906 if ( ! vtl->has_verified_thumbnails )
6908 GSList *pics = NULL;
6909 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6912 gint len = g_slist_length ( pics );
6913 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6914 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6917 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6919 (vik_thr_func) create_thumbnails_thread,
6921 (vik_thr_free_func) thumbnail_create_thread_free,
6929 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6931 return vtl->coord_mode;
6935 * Uniquify the whole layer
6936 * Also requires the layers panel as the names shown there need updating too
6937 * Returns whether the operation was successful or not
6939 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6942 vik_trw_layer_uniquify_tracks ( vtl, vlp );
6943 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
6949 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
6951 vik_coord_convert ( &(wp->coord), *dest_mode );
6954 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6956 vik_track_convert ( tr, *dest_mode );
6959 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6961 if ( vtl->coord_mode != dest_mode )
6963 vtl->coord_mode = dest_mode;
6964 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6965 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6969 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
6971 vtl->menu_selection = selection;
6974 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
6976 return (vtl->menu_selection);
6979 /* ----------- Downloading maps along tracks --------------- */
6981 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6983 /* TODO: calculating based on current size of viewport */
6984 const gdouble w_at_zoom_0_125 = 0.0013;
6985 const gdouble h_at_zoom_0_125 = 0.0011;
6986 gdouble zoom_factor = zoom_level/0.125;
6988 wh->lat = h_at_zoom_0_125 * zoom_factor;
6989 wh->lon = w_at_zoom_0_125 * zoom_factor;
6991 return 0; /* all OK */
6994 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6996 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6997 (dist->lat >= ABS(to->north_south - from->north_south)))
7000 VikCoord *coord = g_malloc(sizeof(VikCoord));
7001 coord->mode = VIK_COORD_LATLON;
7003 if (ABS(gradient) < 1) {
7004 if (from->east_west > to->east_west)
7005 coord->east_west = from->east_west - dist->lon;
7007 coord->east_west = from->east_west + dist->lon;
7008 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
7010 if (from->north_south > to->north_south)
7011 coord->north_south = from->north_south - dist->lat;
7013 coord->north_south = from->north_south + dist->lat;
7014 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
7020 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
7022 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
7023 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
7025 VikCoord *next = from;
7027 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
7029 list = g_list_prepend(list, next);
7035 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
7037 typedef struct _Rect {
7042 #define GLRECT(iter) ((Rect *)((iter)->data))
7045 GList *rects_to_download = NULL;
7048 if (get_download_area_width(vvp, zoom_level, &wh))
7051 GList *iter = tr->trackpoints;
7055 gboolean new_map = TRUE;
7056 VikCoord *cur_coord, tl, br;
7059 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
7061 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7062 rect = g_malloc(sizeof(Rect));
7065 rect->center = *cur_coord;
7066 rects_to_download = g_list_prepend(rects_to_download, rect);
7071 gboolean found = FALSE;
7072 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7073 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7084 GList *fillins = NULL;
7085 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
7086 /* seems that ATM the function get_next_coord works only for LATLON */
7087 if ( cur_coord->mode == VIK_COORD_LATLON ) {
7088 /* fill-ins for far apart points */
7089 GList *cur_rect, *next_rect;
7090 for (cur_rect = rects_to_download;
7091 (next_rect = cur_rect->next) != NULL;
7092 cur_rect = cur_rect->next) {
7093 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
7094 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
7095 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
7099 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
7102 GList *iter = fillins;
7104 cur_coord = (VikCoord *)(iter->data);
7105 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7106 rect = g_malloc(sizeof(Rect));
7109 rect->center = *cur_coord;
7110 rects_to_download = g_list_prepend(rects_to_download, rect);
7115 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7116 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7120 for (iter = fillins; iter; iter = iter->next)
7122 g_list_free(fillins);
7124 if (rects_to_download) {
7125 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
7126 g_free(rect_iter->data);
7127 g_list_free(rects_to_download);
7131 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7134 gint selected_map, default_map;
7135 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
7136 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
7137 gint selected_zoom, default_zoom;
7141 VikTrwLayer *vtl = pass_along[0];
7142 VikLayersPanel *vlp = pass_along[1];
7143 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7144 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
7146 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7147 int num_maps = g_list_length(vmls);
7150 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7154 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
7155 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
7157 gchar **np = map_names;
7158 VikMapsLayer **lp = map_layers;
7159 for (i = 0; i < num_maps; i++) {
7160 gboolean dup = FALSE;
7161 vml = (VikMapsLayer *)(vmls->data);
7162 for (j = 0; j < i; j++) { /* no duplicate allowed */
7163 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
7170 *np++ = vik_maps_layer_get_map_label(vml);
7176 num_maps = lp - map_layers;
7178 for (default_map = 0; default_map < num_maps; default_map++) {
7179 /* TODO: check for parent layer's visibility */
7180 if (VIK_LAYER(map_layers[default_map])->visible)
7183 default_map = (default_map == num_maps) ? 0 : default_map;
7185 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
7186 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
7187 if (cur_zoom == zoom_vals[default_zoom])
7190 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
7192 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
7195 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7198 for (i = 0; i < num_maps; i++)
7199 g_free(map_names[i]);
7207 /**** lowest waypoint number calculation ***/
7208 static gint highest_wp_number_name_to_number(const gchar *name) {
7209 if ( strlen(name) == 3 ) {
7211 if ( n < 100 && name[0] != '0' )
7213 if ( n < 10 && name[0] != '0' )
7221 static void highest_wp_number_reset(VikTrwLayer *vtl)
7223 vtl->highest_wp_number = -1;
7226 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
7228 /* if is bigger that top, add it */
7229 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
7230 if ( new_wp_num > vtl->highest_wp_number )
7231 vtl->highest_wp_number = new_wp_num;
7234 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
7236 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
7237 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
7238 if ( vtl->highest_wp_number == old_wp_num ) {
7240 vtl->highest_wp_number--;
7242 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7243 /* search down until we find something that *does* exist */
7245 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
7246 vtl->highest_wp_number--;
7247 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7252 /* get lowest unused number */
7253 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
7256 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
7258 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
7259 return g_strdup(buf);