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 #ifdef VIK_CONFIG_GOOGLE
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
80 #define VIK_TRW_LAYER_TRACK_GC 16
81 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
82 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
83 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
84 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
85 #define VIK_TRW_LAYER_TRACK_GC_SLOW 13
86 #define VIK_TRW_LAYER_TRACK_GC_AVER 14
87 #define VIK_TRW_LAYER_TRACK_GC_FAST 15
89 #define DRAWMODE_BY_TRACK 0
90 #define DRAWMODE_BY_SPEED 1
91 #define DRAWMODE_ALL_BLACK 2
92 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
93 // as we are (re)calculating the colour for every point
98 /* this is how it knows when you click if you are clicking close to a trackpoint. */
99 #define TRACKPOINT_SIZE_APPROX 5
100 #define WAYPOINT_SIZE_APPROX 5
102 #define MIN_STOP_LENGTH 15
103 #define MAX_STOP_LENGTH 86400
104 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
105 /* this is multiplied by user-inputted value from 1-100. */
107 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
109 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
111 FS_XX_SMALL = 0, // 'xx-small'
114 FS_MEDIUM, // DEFAULT
121 struct _VikTrwLayer {
124 GHashTable *tracks_iters;
125 GHashTable *waypoints_iters;
126 GHashTable *waypoints;
127 GtkTreeIter waypoints_iter, tracks_iter;
128 gboolean tracks_visible, waypoints_visible;
131 guint8 drawpoints_size;
132 guint8 drawelevation;
133 guint8 elevation_factor;
137 guint8 drawdirections;
138 guint8 drawdirections_size;
139 guint8 line_thickness;
140 guint8 bg_line_thickness;
144 gboolean wp_draw_symbols;
145 font_size_t wp_font_size;
147 gdouble track_draw_speed_factor;
149 GdkGC *current_track_gc;
150 // Separate GC for a track's potential new point as drawn via separate method
151 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
152 GdkGC *current_track_newpoint_gc;
155 GdkGC *waypoint_text_gc;
156 GdkGC *waypoint_bg_gc;
157 VikTrack *current_track;
158 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
159 gboolean draw_sync_done;
160 gboolean draw_sync_do;
162 VikCoordMode coord_mode;
164 /* wp editing tool */
165 VikWaypoint *current_wp;
166 gpointer current_wp_id;
168 gboolean waypoint_rightclick;
170 /* track editing tool */
172 VikTrack *current_tp_track;
173 gpointer current_tp_id;
174 VikTrwLayerTpwin *tpwin;
176 /* track editing tool -- more specifically, moving tps */
179 /* route finder tool */
180 gboolean route_finder_started;
181 VikCoord route_finder_coord;
182 gboolean route_finder_check_added_track;
183 VikTrack *route_finder_added_track;
184 VikTrack *route_finder_current_track;
185 gboolean route_finder_append;
192 guint16 image_cache_size;
194 /* for waypoint text */
195 PangoLayout *wplabellayout;
197 gboolean has_verified_thumbnails;
199 GtkMenu *wp_right_click_menu;
200 GtkMenu *track_right_click_menu;
203 VikStdLayerMenuItem menu_selection;
205 gint highest_wp_number;
208 /* A caached waypoint image. */
211 gchar *image; /* filename */
214 struct DrawingParams {
218 guint16 width, height;
219 gdouble cc; // Cosine factor in track directions
220 gdouble ss; // Sine factor in track directions
221 const VikCoord *center;
223 gboolean one_zone, lat_lon;
224 gdouble ce1, ce2, cn1, cn2;
227 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
229 static void trw_layer_delete_item ( gpointer pass_along[6] );
230 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
231 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
233 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
234 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] );
235 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
237 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
238 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
240 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
241 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
243 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
244 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
245 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
246 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
247 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
248 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
249 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
250 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
251 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
252 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
253 static void trw_layer_append_track ( gpointer pass_along[6] );
254 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
255 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
256 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
257 static void trw_layer_split_segments ( gpointer pass_along[6] );
258 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
259 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
260 static void trw_layer_reverse ( gpointer pass_along[6] );
261 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
262 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
263 static void trw_layer_show_picture ( gpointer pass_along[6] );
264 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
266 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
267 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
268 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
269 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
270 static void trw_layer_new_wp ( gpointer lav[2] );
271 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
272 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
273 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
274 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
275 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
276 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
277 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
278 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
279 #ifdef VIK_CONFIG_GEOTAG
280 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
281 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
282 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
283 static void trw_layer_geotagging ( gpointer lav[2] );
285 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
286 #ifdef VIK_CONFIG_GOOGLE
287 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
289 #ifdef VIK_CONFIG_OPENSTREETMAP
290 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
292 #ifdef VIK_CONFIG_GEOCACHES
293 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
295 #ifdef VIK_CONFIG_GEOTAG
296 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
298 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
299 static void trw_layer_gps_upload ( gpointer lav[2] );
302 static void trw_layer_properties_item ( gpointer pass_along[7] );
303 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
304 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
306 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
307 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
308 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
310 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
311 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
312 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
313 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
315 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
316 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
317 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
318 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
319 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
320 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
321 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
322 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
323 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
324 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
325 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
326 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
327 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
328 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
329 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
330 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
331 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
332 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
333 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
334 #ifdef VIK_CONFIG_GOOGLE
335 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
336 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
339 static void cached_pixbuf_free ( CachedPixbuf *cp );
340 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
342 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
343 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
345 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
346 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
348 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
349 static void highest_wp_number_reset(VikTrwLayer *vtl);
350 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
351 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
353 // Note for the following tool GtkRadioActionEntry texts:
354 // the very first text value is an internal name not displayed anywhere
355 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
356 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
357 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
358 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
359 static VikToolInterface trw_layer_tools[] = {
360 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
361 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
362 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
364 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
366 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
367 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
368 (VikToolMouseFunc) tool_new_track_click,
369 (VikToolMouseMoveFunc) tool_new_track_move,
370 (VikToolMouseFunc) tool_new_track_release,
371 (VikToolKeyFunc) tool_new_track_key_press,
372 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
373 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
375 { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "<control><shift>B", N_("Begin Track"), 0 },
376 (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
377 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL,
379 GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
381 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
382 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
383 (VikToolMouseFunc) tool_edit_waypoint_click,
384 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
385 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
387 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
389 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
390 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
391 (VikToolMouseFunc) tool_edit_trackpoint_click,
392 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
393 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
395 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
397 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
398 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
399 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
401 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
403 #ifdef VIK_CONFIG_GOOGLE
404 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
405 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
406 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
408 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
411 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
413 /****** PARAMETERS ******/
415 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
416 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
418 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
419 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
421 #define MIN_POINT_SIZE 2
422 #define MAX_POINT_SIZE 10
424 #define MIN_ARROW_SIZE 3
425 #define MAX_ARROW_SIZE 20
427 static VikLayerParamScale params_scales[] = {
428 /* min max step digits */
429 { 1, 10, 1, 0 }, /* line_thickness */
430 { 0, 100, 1, 0 }, /* track draw speed factor */
431 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
432 /* 5 * step == how much to turn */
433 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
434 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
435 { 5, 500, 5, 0 }, // 5: image cache_size - " "
436 { 0, 8, 1, 0 }, // 6: Background line thickness
437 { 1, 64, 1, 0 }, /* wpsize */
438 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
439 { 1, 100, 1, 0 }, // 9: elevation factor
440 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
441 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
444 static gchar* params_font_sizes[] = {
445 N_("Extra Extra Small"),
451 N_("Extra Extra Large"),
454 VikLayerParam trw_layer_params[] = {
455 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
456 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
458 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
459 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
460 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
461 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
462 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
463 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
464 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
465 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
466 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
468 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
469 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
471 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
472 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
473 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
475 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
476 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
477 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
478 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
479 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
480 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
481 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
482 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
483 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
485 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
486 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
487 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
488 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
491 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
493 // Sublayer visibilities
530 *** 1) Add to trw_layer_params and enumeration
531 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
534 /****** END PARAMETERS ******/
536 static VikTrwLayer* trw_layer_new ( gint drawmode );
537 /* Layer Interface function definitions */
538 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
539 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
540 static void trw_layer_free ( VikTrwLayer *trwlayer );
541 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
542 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
543 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
544 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
545 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
546 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
547 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
548 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
549 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
550 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
551 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
552 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
553 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
554 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
555 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
556 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
557 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
558 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
559 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
560 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
561 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
562 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
563 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
564 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
565 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
566 /* End Layer Interface function definitions */
568 VikLayerInterface vik_trw_layer_interface = {
575 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
579 params_groups, /* params_groups */
580 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
584 (VikLayerFuncCreate) trw_layer_create,
585 (VikLayerFuncRealize) trw_layer_realize,
586 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
587 (VikLayerFuncFree) trw_layer_free,
589 (VikLayerFuncProperties) NULL,
590 (VikLayerFuncDraw) trw_layer_draw,
591 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
593 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
594 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
596 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
597 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
599 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
600 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
601 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
602 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
603 (VikLayerFuncLayerSelected) trw_layer_selected,
605 (VikLayerFuncMarshall) trw_layer_marshall,
606 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
608 (VikLayerFuncSetParam) trw_layer_set_param,
609 (VikLayerFuncGetParam) trw_layer_get_param,
611 (VikLayerFuncReadFileData) a_gpspoint_read_file,
612 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
614 (VikLayerFuncDeleteItem) trw_layer_del_item,
615 (VikLayerFuncCutItem) trw_layer_cut_item,
616 (VikLayerFuncCopyItem) trw_layer_copy_item,
617 (VikLayerFuncPasteItem) trw_layer_paste_item,
618 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
620 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
622 (VikLayerFuncSelectClick) trw_layer_select_click,
623 (VikLayerFuncSelectMove) trw_layer_select_move,
624 (VikLayerFuncSelectRelease) trw_layer_select_release,
625 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
634 GType vik_trw_layer_get_type ()
636 static GType vtl_type = 0;
640 static const GTypeInfo vtl_info =
642 sizeof (VikTrwLayerClass),
643 NULL, /* base_init */
644 NULL, /* base_finalize */
645 NULL, /* class init */
646 NULL, /* class_finalize */
647 NULL, /* class_data */
648 sizeof (VikTrwLayer),
650 NULL /* instance init */
652 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
658 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
660 static gpointer pass_along[6];
666 pass_along[1] = NULL;
667 pass_along[2] = GINT_TO_POINTER (subtype);
668 pass_along[3] = sublayer;
669 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
670 pass_along[5] = NULL;
672 trw_layer_delete_item ( pass_along );
675 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
677 static gpointer pass_along[6];
683 pass_along[1] = NULL;
684 pass_along[2] = GINT_TO_POINTER (subtype);
685 pass_along[3] = sublayer;
686 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
687 pass_along[5] = NULL;
689 trw_layer_copy_item_cb(pass_along);
690 trw_layer_cut_item_cb(pass_along);
693 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
695 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
696 gint subtype = GPOINTER_TO_INT (pass_along[2]);
697 gpointer * sublayer = pass_along[3];
701 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
705 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
706 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
707 if ( wp && wp->name )
710 name = NULL; // Broken :(
713 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
714 if ( trk && trk->name )
717 name = NULL; // Broken :(
720 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
721 subtype, len, name, data);
725 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
727 trw_layer_copy_item_cb(pass_along);
728 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
729 trw_layer_delete_item(pass_along);
732 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
743 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
745 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
746 // 'Simple' memory copy of byte array from the marshalling above
747 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
748 fi = g_malloc ( *len );
750 memcpy(fi->data, id, il);
753 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
754 // less magic than before...
755 *len = sizeof(FlatItem) + 1 + il;
756 fi = g_malloc ( *len );
758 memcpy(fi->data, id, il);
762 *item = (guint8 *)fi;
765 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
767 FlatItem *fi = (FlatItem *) item;
769 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
774 w = vik_waypoint_unmarshall(fi->data, fi->len);
775 // When copying - we'll create a new name based on the original
776 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
777 vik_trw_layer_add_waypoint ( vtl, name, w );
778 waypoint_convert (NULL, w, &vtl->coord_mode);
780 // Consider if redraw necessary for the new item
781 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
782 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
785 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
790 t = vik_track_unmarshall(fi->data, fi->len);
791 // When copying - we'll create a new name based on the original
792 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
793 vik_trw_layer_add_track ( vtl, name, t );
794 track_convert (name, t, &vtl->coord_mode);
796 // Consider if redraw necessary for the new item
797 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
798 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
804 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
811 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
815 case PARAM_TV: vtl->tracks_visible = data.b; break;
816 case PARAM_WV: vtl->waypoints_visible = data.b; break;
817 case PARAM_DM: vtl->drawmode = data.u; break;
818 case PARAM_DP: vtl->drawpoints = data.b; break;
820 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
821 vtl->drawpoints_size = data.u;
823 case PARAM_DE: vtl->drawelevation = data.b; break;
824 case PARAM_DS: vtl->drawstops = data.b; break;
825 case PARAM_DL: vtl->drawlines = data.b; break;
826 case PARAM_DD: vtl->drawdirections = data.b; break;
828 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
829 vtl->drawdirections_size = data.u;
831 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
832 vtl->stop_length = data.u;
834 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
835 vtl->elevation_factor = data.u;
837 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
839 vtl->line_thickness = data.u;
840 trw_layer_new_track_gcs ( vtl, vp );
843 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
845 vtl->bg_line_thickness = data.u;
846 trw_layer_new_track_gcs ( vtl, vp );
849 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
850 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
851 case PARAM_DLA: vtl->drawlabels = data.b; break;
852 case PARAM_DI: vtl->drawimages = data.b; break;
853 case PARAM_IS: if ( data.u != vtl->image_size )
855 vtl->image_size = data.u;
856 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
857 g_queue_free ( vtl->image_cache );
858 vtl->image_cache = g_queue_new ();
861 case PARAM_IA: vtl->image_alpha = data.u; break;
862 case PARAM_ICS: vtl->image_cache_size = data.u;
863 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
864 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
866 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
867 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
868 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
869 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
870 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
871 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
872 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
873 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
878 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
880 VikLayerParamData rv;
883 case PARAM_TV: rv.b = vtl->tracks_visible; break;
884 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
885 case PARAM_DM: rv.u = vtl->drawmode; break;
886 case PARAM_DP: rv.b = vtl->drawpoints; break;
887 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
888 case PARAM_DE: rv.b = vtl->drawelevation; break;
889 case PARAM_EF: rv.u = vtl->elevation_factor; break;
890 case PARAM_DS: rv.b = vtl->drawstops; break;
891 case PARAM_SL: rv.u = vtl->stop_length; break;
892 case PARAM_DL: rv.b = vtl->drawlines; break;
893 case PARAM_DD: rv.b = vtl->drawdirections; break;
894 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
895 case PARAM_LT: rv.u = vtl->line_thickness; break;
896 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
897 case PARAM_DLA: rv.b = vtl->drawlabels; break;
898 case PARAM_DI: rv.b = vtl->drawimages; break;
899 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
900 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
901 case PARAM_IS: rv.u = vtl->image_size; break;
902 case PARAM_IA: rv.u = vtl->image_alpha; break;
903 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
904 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
905 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
906 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
907 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
908 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
909 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
910 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
911 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
916 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
927 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
928 a_gpx_write_file(vtl, f, NULL);
929 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
932 g_file_get_contents(tmpname, &dd, &dl, NULL);
933 *len = sizeof(pl) + pl + dl;
934 *data = g_malloc(*len);
935 memcpy(*data, &pl, sizeof(pl));
936 memcpy(*data + sizeof(pl), pd, pl);
937 memcpy(*data + sizeof(pl) + pl, dd, dl);
946 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
948 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
954 memcpy(&pl, data, sizeof(pl));
956 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
959 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
960 g_critical("couldn't open temp file");
963 fwrite(data, len - pl - sizeof(pl), 1, f);
965 a_gpx_read_file(rv, f);
973 // Keep interesting hash function at least visible
975 static guint strcase_hash(gconstpointer v)
977 // 31 bit hash function
980 gchar s[128]; // malloc is too slow for reading big files
983 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
984 p[i] = toupper(t[i]);
990 for (p += 1; *p != '\0'; p++)
991 h = (h << 5) - h + *p;
998 static VikTrwLayer* trw_layer_new ( gint drawmode )
1000 if (trw_layer_params[PARAM_DM].widget_data == NULL)
1001 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
1002 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
1003 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
1005 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1006 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1008 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1009 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1011 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1012 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1013 // and with normal PC processing capabilities - it has negligibile performance impact
1014 // This also minimized the amount of rework - as the management of the hash tables already exists.
1016 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1017 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1018 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1020 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1021 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1022 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1023 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1025 /* TODO: constants at top */
1026 rv->waypoints_visible = rv->tracks_visible = TRUE;
1027 rv->drawmode = drawmode;
1028 rv->drawpoints = TRUE;
1029 rv->drawpoints_size = MIN_POINT_SIZE;
1030 rv->drawdirections_size = 5;
1031 rv->drawstops = FALSE;
1032 rv->drawelevation = FALSE;
1033 rv->elevation_factor = 30;
1034 rv->stop_length = 60;
1035 rv->drawlines = TRUE;
1036 rv->wplabellayout = NULL;
1037 rv->wp_right_click_menu = NULL;
1038 rv->track_right_click_menu = NULL;
1039 rv->waypoint_gc = NULL;
1040 rv->waypoint_text_gc = NULL;
1041 rv->waypoint_bg_gc = NULL;
1042 rv->track_gc = NULL;
1043 rv->track_draw_speed_factor = 30.0;
1044 rv->line_thickness = 1;
1045 rv->bg_line_thickness = 0;
1046 rv->current_wp = NULL;
1047 rv->current_wp_id = NULL;
1048 rv->current_track = NULL;
1049 rv->current_tpl = NULL;
1050 rv->current_tp_track = NULL;
1051 rv->current_tp_id = NULL;
1052 rv->moving_tp = FALSE;
1053 rv->moving_wp = FALSE;
1055 rv->draw_sync_done = TRUE;
1056 rv->draw_sync_do = TRUE;
1058 rv->route_finder_started = FALSE;
1059 rv->route_finder_check_added_track = FALSE;
1060 rv->route_finder_current_track = NULL;
1061 rv->route_finder_append = FALSE;
1063 rv->waypoint_rightclick = FALSE;
1065 rv->image_cache = g_queue_new();
1066 rv->image_size = 64;
1067 rv->image_alpha = 255;
1068 rv->image_cache_size = 300;
1069 rv->drawimages = TRUE;
1070 rv->drawlabels = TRUE;
1075 static void trw_layer_free ( VikTrwLayer *trwlayer )
1077 g_hash_table_destroy(trwlayer->waypoints);
1078 g_hash_table_destroy(trwlayer->tracks);
1080 /* ODC: replace with GArray */
1081 trw_layer_free_track_gcs ( trwlayer );
1083 if ( trwlayer->wp_right_click_menu )
1084 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1086 if ( trwlayer->track_right_click_menu )
1087 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1089 if ( trwlayer->wplabellayout != NULL)
1090 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1092 if ( trwlayer->waypoint_gc != NULL )
1093 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1095 if ( trwlayer->waypoint_text_gc != NULL )
1096 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1098 if ( trwlayer->waypoint_bg_gc != NULL )
1099 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1101 if ( trwlayer->tpwin != NULL )
1102 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1104 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1105 g_queue_free ( trwlayer->image_cache );
1108 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1112 dp->xmpp = vik_viewport_get_xmpp ( vp );
1113 dp->ympp = vik_viewport_get_ympp ( vp );
1114 dp->width = vik_viewport_get_width ( vp );
1115 dp->height = vik_viewport_get_height ( vp );
1116 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1117 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1119 dp->center = vik_viewport_get_center ( vp );
1120 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1121 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1126 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1127 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1128 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1130 dp->ce1 = dp->center->east_west-w2;
1131 dp->ce2 = dp->center->east_west+w2;
1132 dp->cn1 = dp->center->north_south-h2;
1133 dp->cn2 = dp->center->north_south+h2;
1134 } else if ( dp->lat_lon ) {
1135 VikCoord upperleft, bottomright;
1136 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1137 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1138 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1139 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1140 dp->ce1 = upperleft.east_west;
1141 dp->ce2 = bottomright.east_west;
1142 dp->cn1 = bottomright.north_south;
1143 dp->cn2 = upperleft.north_south;
1146 dp->track_gc_iter = 0;
1150 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1151 * Here a simple traffic like light colour system is used:
1152 * . slow points are red
1153 * . average is yellow
1154 * . fast points are green
1156 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1159 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1160 if ( average_speed > 0 ) {
1161 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1162 if ( rv < low_speed )
1163 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1164 else if ( rv > high_speed )
1165 return VIK_TRW_LAYER_TRACK_GC_FAST;
1167 return VIK_TRW_LAYER_TRACK_GC_AVER;
1170 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1173 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1175 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1176 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1177 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1178 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1181 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1183 /* TODO: this function is a mess, get rid of any redundancy */
1184 GList *list = track->trackpoints;
1186 gboolean useoldvals = TRUE;
1188 gboolean drawpoints;
1190 gboolean drawelevation;
1191 gdouble min_alt, max_alt, alt_diff = 0;
1193 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1194 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1197 if ( dp->vtl->drawelevation )
1199 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1200 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1201 alt_diff = max_alt - min_alt;
1204 if ( ! track->visible )
1207 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1208 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1209 trw_layer_draw_track ( name, track, dp, TRUE );
1211 if ( drawing_white_background )
1212 drawpoints = drawstops = FALSE;
1214 drawpoints = dp->vtl->drawpoints;
1215 drawstops = dp->vtl->drawstops;
1218 gboolean drawing_highlight = FALSE;
1219 /* Current track - used for creation */
1220 if ( track == dp->vtl->current_track )
1221 main_gc = dp->vtl->current_track_gc;
1223 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1224 /* Draw all tracks of the layer in special colour */
1225 /* if track is member of selected layer or is the current selected track
1226 then draw in the highlight colour.
1227 NB this supercedes the drawmode */
1228 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1229 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1230 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1231 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1232 drawing_highlight = TRUE;
1235 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1236 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1238 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1242 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1243 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1245 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1250 int x, y, oldx, oldy;
1251 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1253 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1255 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1257 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1259 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1260 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1266 gdouble average_speed = 0.0;
1267 gdouble low_speed = 0.0;
1268 gdouble high_speed = 0.0;
1269 // If necessary calculate these values - which is done only once per track redraw
1270 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1271 // the percentage factor away from the average speed determines transistions between the levels
1272 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1273 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1274 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1277 while ((list = g_list_next(list)))
1279 tp = VIK_TRACKPOINT(list->data);
1280 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1282 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1283 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1284 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1285 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1286 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1288 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1291 * If points are the same in display coordinates, don't draw.
1293 if ( useoldvals && x == oldx && y == oldy )
1295 // Still need to process points to ensure 'stops' are drawn if required
1296 if ( drawstops && drawpoints && ! drawing_white_background && list->next &&
1297 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1298 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 );
1303 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1304 if ( drawpoints || dp->vtl->drawlines ) {
1305 // setup main_gc for both point and line drawing
1306 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1307 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1308 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1312 if ( drawpoints && ! drawing_white_background )
1317 * The concept of drawing stops is that a trackpoint
1318 * that is if the next trackpoint has a timestamp far into
1319 * the future, we draw a circle of 6x trackpoint size,
1320 * instead of a rectangle of 2x trackpoint size.
1321 * This is drawn first so the trackpoint will be drawn on top
1324 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1325 /* Stop point. Draw 6x circle. */
1326 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 );
1328 /* Regular point - draw 2x square. */
1329 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1332 /* Final point - draw 4x circle. */
1333 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 );
1336 if ((!tp->newsegment) && (dp->vtl->drawlines))
1339 /* UTM only: zone check */
1340 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1341 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1344 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1346 if ( drawing_white_background ) {
1347 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1351 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1353 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1355 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1360 tmp[1].y = oldy-FIXALTITUDE(list->data);
1362 tmp[2].y = y-FIXALTITUDE(list->next->data);
1367 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1368 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1370 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1371 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1373 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1378 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1379 // Draw an arrow at the mid point to show the direction of the track
1380 // Code is a rework from vikwindow::draw_ruler()
1381 gint midx = (oldx + x) / 2;
1382 gint midy = (oldy + y) / 2;
1384 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1385 // Avoid divide by zero and ensure at least 1 pixel big
1387 gdouble dx = (oldx - midx) / len;
1388 gdouble dy = (oldy - midy) / len;
1389 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1390 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 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1402 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1403 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1405 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1407 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1408 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1409 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1413 * If points are the same in display coordinates, don't draw.
1415 if ( x != oldx || y != oldy )
1417 if ( drawing_white_background )
1418 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1420 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1426 * If points are the same in display coordinates, don't draw.
1428 if ( x != oldx && y != oldy )
1430 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1431 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1439 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1440 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
1441 dp->track_gc_iter = 0;
1444 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1445 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1447 trw_layer_draw_track ( name, track, dp, FALSE );
1450 static void cached_pixbuf_free ( CachedPixbuf *cp )
1452 g_object_unref ( G_OBJECT(cp->pixbuf) );
1453 g_free ( cp->image );
1456 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1458 return strcmp ( cp->image, name );
1461 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1464 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1465 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1466 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1469 GdkPixbuf *sym = NULL;
1470 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1472 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1474 if ( wp->image && dp->vtl->drawimages )
1476 GdkPixbuf *pixbuf = NULL;
1479 if ( dp->vtl->image_alpha == 0)
1482 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1484 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1487 gchar *image = wp->image;
1488 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1489 if ( ! regularthumb )
1491 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1492 image = "\x12\x00"; /* this shouldn't occur naturally. */
1496 CachedPixbuf *cp = NULL;
1497 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1498 if ( dp->vtl->image_size == 128 )
1499 cp->pixbuf = regularthumb;
1502 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1503 g_assert ( cp->pixbuf );
1504 g_object_unref ( G_OBJECT(regularthumb) );
1506 cp->image = g_strdup ( image );
1508 /* needed so 'click picture' tool knows how big the pic is; we don't
1509 * store it in cp because they may have been freed already. */
1510 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1511 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1513 g_queue_push_head ( dp->vtl->image_cache, cp );
1514 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1515 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1517 pixbuf = cp->pixbuf;
1521 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1527 w = gdk_pixbuf_get_width ( pixbuf );
1528 h = gdk_pixbuf_get_height ( pixbuf );
1530 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1532 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1533 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1534 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1535 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1536 // Highlighted - so draw a little border around the chosen one
1537 // single line seems a little weak so draw 2 of them
1538 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1539 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1540 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1541 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1544 if ( dp->vtl->image_alpha == 255 )
1545 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1547 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1549 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1553 /* DRAW ACTUAL DOT */
1554 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1555 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 );
1557 else if ( wp == dp->vtl->current_wp ) {
1558 switch ( dp->vtl->wp_symbol ) {
1559 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;
1560 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;
1561 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;
1562 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 );
1563 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 );
1567 switch ( dp->vtl->wp_symbol ) {
1568 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;
1569 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;
1570 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;
1571 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 );
1572 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;
1576 if ( dp->vtl->drawlabels )
1578 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1579 gint label_x, label_y;
1581 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1583 // Could this stored in the waypoint rather than recreating each pass?
1584 gchar *fsize = NULL;
1585 switch (dp->vtl->wp_font_size) {
1586 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1587 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1588 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1589 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1590 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1591 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1592 default: fsize = g_strdup ( "medium" ); break;
1595 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1597 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1598 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1600 // Fallback if parse failure
1601 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1603 g_free ( wp_label_markup );
1606 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1607 label_x = x - width/2;
1609 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1611 label_y = y - dp->vtl->wp_size - height - 2;
1613 /* if highlight mode on, then draw background text in highlight colour */
1614 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1615 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1616 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1617 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1618 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1620 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1623 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1625 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1630 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1632 static struct DrawingParams dp;
1633 g_assert ( l != NULL );
1635 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1637 if ( l->tracks_visible )
1638 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1640 if (l->waypoints_visible)
1641 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1644 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1647 if ( vtl->track_bg_gc )
1649 g_object_unref ( vtl->track_bg_gc );
1650 vtl->track_bg_gc = NULL;
1652 if ( vtl->current_track_gc )
1654 g_object_unref ( vtl->current_track_gc );
1655 vtl->current_track_gc = NULL;
1657 if ( vtl->current_track_newpoint_gc )
1659 g_object_unref ( vtl->current_track_newpoint_gc );
1660 vtl->current_track_newpoint_gc = NULL;
1663 if ( ! vtl->track_gc )
1665 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1666 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1667 g_array_free ( vtl->track_gc, TRUE );
1668 vtl->track_gc = NULL;
1671 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1673 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1674 gint width = vtl->line_thickness;
1676 if ( vtl->track_gc )
1677 trw_layer_free_track_gcs ( vtl );
1679 if ( vtl->track_bg_gc )
1680 g_object_unref ( vtl->track_bg_gc );
1681 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1683 // Ensure new track drawing heeds line thickness setting
1684 // however always have a minium of 2, as 1 pixel is really narrow
1685 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1687 if ( vtl->current_track_gc )
1688 g_object_unref ( vtl->current_track_gc );
1689 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1690 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1692 // 'newpoint' gc is exactly the same as the current track gc
1693 if ( vtl->current_track_newpoint_gc )
1694 g_object_unref ( vtl->current_track_newpoint_gc );
1695 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1696 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1698 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1700 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1702 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1703 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1704 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1705 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1706 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1707 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1708 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1709 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1710 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1711 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1713 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1715 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1717 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1718 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1719 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1721 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1724 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1726 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1727 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1729 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1730 /* early exit, as the rest is GUI related */
1734 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1735 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1737 trw_layer_new_track_gcs ( rv, vp );
1739 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1740 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1741 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1742 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1744 rv->has_verified_thumbnails = FALSE;
1745 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1747 rv->wp_draw_symbols = TRUE;
1748 rv->wp_font_size = FS_MEDIUM;
1750 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1752 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1757 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1759 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1761 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1762 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 );
1764 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 );
1767 *new_iter = *((GtkTreeIter *) pass_along[1]);
1768 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1770 if ( ! track->visible )
1771 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1774 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1776 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1777 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1778 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 );
1780 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 );
1783 *new_iter = *((GtkTreeIter *) pass_along[1]);
1784 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1786 if ( ! wp->visible )
1787 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1791 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1794 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1796 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1797 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1799 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1801 if ( ! vtl->tracks_visible )
1802 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1804 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1806 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1807 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1809 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1812 if ( ! vtl->waypoints_visible )
1813 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1815 pass_along[0] = &(vtl->waypoints_iter);
1816 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1818 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1822 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1826 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1827 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1828 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1830 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1832 return (t->visible ^= 1);
1836 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1838 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1840 return (t->visible ^= 1);
1849 * Return a property about tracks for this layer
1851 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1853 return vtl->line_thickness;
1856 // Structure to hold multiple track information for a layer
1865 * Build up layer multiple track information via updating the tooltip_tracks structure
1867 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1869 tt->length = tt->length + vik_track_get_length (tr);
1871 // Ensure times are available
1872 if ( tr->trackpoints &&
1873 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1874 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1877 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1878 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1880 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1881 // Hence initialize to the first 'proper' value
1882 if ( tt->start_time == 0 )
1883 tt->start_time = t1;
1884 if ( tt->end_time == 0 )
1887 // Update find the earliest / last times
1888 if ( t1 < tt->start_time )
1889 tt->start_time = t1;
1890 if ( t2 > tt->end_time )
1893 // Keep track of total time
1894 // there maybe gaps within a track (eg segments)
1895 // but this should be generally good enough for a simple indicator
1896 tt->duration = tt->duration + (int)(t2-t1);
1901 * Generate tooltip text for the layer.
1902 * This is relatively complicated as it considers information for
1903 * no tracks, a single track or multiple tracks
1904 * (which may or may not have timing information)
1906 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1917 static gchar tmp_buf[128];
1920 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1922 // Safety check - I think these should always be valid
1923 if ( vtl->tracks && vtl->waypoints ) {
1924 tooltip_tracks tt = { 0.0, 0, 0 };
1925 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1927 GDate* gdate_start = g_date_new ();
1928 g_date_set_time_t (gdate_start, tt.start_time);
1930 GDate* gdate_end = g_date_new ();
1931 g_date_set_time_t (gdate_end, tt.end_time);
1933 if ( g_date_compare (gdate_start, gdate_end) ) {
1934 // Dates differ so print range on separate line
1935 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1936 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1937 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1940 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1941 if ( tt.start_time != 0 )
1942 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1946 if ( tt.length > 0.0 ) {
1947 gdouble len_in_units;
1949 // Setup info dependent on distance units
1950 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1951 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1952 len_in_units = VIK_METERS_TO_MILES(tt.length);
1955 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1956 len_in_units = tt.length/1000.0;
1959 // Timing information if available
1961 if ( tt.duration > 0 ) {
1962 g_snprintf (tbuf1, sizeof(tbuf1),
1963 _(" in %d:%02d hrs:mins"),
1964 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1966 g_snprintf (tbuf2, sizeof(tbuf2),
1967 _("\n%sTotal Length %.1f %s%s"),
1968 tbuf3, len_in_units, tbuf4, tbuf1);
1971 // Put together all the elements to form compact tooltip text
1972 g_snprintf (tmp_buf, sizeof(tmp_buf),
1973 _("Tracks: %d - Waypoints: %d%s"),
1974 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1976 g_date_free (gdate_start);
1977 g_date_free (gdate_end);
1984 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1988 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1989 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1990 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1992 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1994 // Could be a better way of handling strings - but this works...
1995 gchar time_buf1[20];
1996 gchar time_buf2[20];
1997 time_buf1[0] = '\0';
1998 time_buf2[0] = '\0';
1999 static gchar tmp_buf[100];
2000 // Compact info: Short date eg (11/20/99), duration and length
2001 // Hopefully these are the things that are most useful and so promoted into the tooltip
2002 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2003 // %x The preferred date representation for the current locale without the time.
2004 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2005 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2006 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2008 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2011 // Get length and consider the appropriate distance units
2012 gdouble tr_len = vik_track_get_length(tr);
2013 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2014 switch (dist_units) {
2015 case VIK_UNITS_DISTANCE_KILOMETRES:
2016 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2018 case VIK_UNITS_DISTANCE_MILES:
2019 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2028 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2030 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2031 // NB It's OK to return NULL
2042 * Function to show basic track point information on the statusbar
2044 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2047 switch (a_vik_get_units_height ()) {
2048 case VIK_UNITS_HEIGHT_FEET:
2049 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2052 //VIK_UNITS_HEIGHT_METRES:
2053 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2058 if ( trkpt->has_timestamp ) {
2059 // Compact date time format
2060 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2064 // Position is put later on, as this bit may not be seen if the display is not big enough,
2065 // one can easily use the current pointer position to see this if needed
2066 gchar *lat = NULL, *lon = NULL;
2067 static struct LatLon ll;
2068 vik_coord_to_latlon (&(trkpt->coord), &ll);
2069 a_coords_latlon_to_string ( &ll, &lat, &lon );
2072 // Again is put later on, as this bit may not be seen if the display is not big enough
2073 // trackname can be seen from the treeview (when enabled)
2074 // Also name could be very long to not leave room for anything else
2077 if ( vtl->current_tp_track ) {
2078 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2081 // Combine parts to make overall message
2082 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2083 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2090 * Function to show basic waypoint information on the statusbar
2092 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2095 switch (a_vik_get_units_height ()) {
2096 case VIK_UNITS_HEIGHT_FEET:
2097 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2100 //VIK_UNITS_HEIGHT_METRES:
2101 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2105 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2106 // one can easily use the current pointer position to see this if needed
2107 gchar *lat = NULL, *lon = NULL;
2108 static struct LatLon ll;
2109 vik_coord_to_latlon (&(wpt->coord), &ll);
2110 a_coords_latlon_to_string ( &ll, &lat, &lon );
2112 // Combine parts to make overall message
2115 // Add comment if available
2116 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2118 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2119 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2126 * General layer selection function, find out which bit is selected and take appropriate action
2128 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2131 l->current_wp = NULL;
2132 l->current_wp_id = NULL;
2133 trw_layer_cancel_current_tp ( l, FALSE );
2136 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2140 case VIK_TREEVIEW_TYPE_LAYER:
2142 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2143 /* Mark for redraw */
2148 case VIK_TREEVIEW_TYPE_SUBLAYER:
2152 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2154 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2155 /* Mark for redraw */
2159 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2161 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2162 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2163 /* Mark for redraw */
2167 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2169 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2170 /* Mark for redraw */
2174 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2176 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2178 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2179 // Show some waypoint info
2180 set_statusbar_msg_info_wpt ( l, wpt );
2181 /* Mark for redraw */
2188 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2197 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2202 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2207 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2209 return l->waypoints;
2213 * ATM use a case sensitive find
2214 * Finds the first one
2216 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2218 if ( wp && wp->name )
2219 if ( ! strcmp ( wp->name, name ) )
2225 * Get waypoint by name - not guaranteed to be unique
2226 * Finds the first one
2228 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2230 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2234 * ATM use a case sensitive find
2235 * Finds the first one
2237 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2239 if ( trk && trk->name )
2240 if ( ! strcmp ( trk->name, name ) )
2246 * Get track by name - not guaranteed to be unique
2247 * Finds the first one
2249 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2251 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2254 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2256 static VikCoord fixme;
2257 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2258 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2259 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2260 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2261 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2262 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2263 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2264 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2265 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2268 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2270 GList *tr = trk->trackpoints;
2271 static VikCoord fixme;
2275 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2276 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2277 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2278 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2279 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2280 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2281 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2282 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2283 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2288 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2290 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2291 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2293 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2294 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2295 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2296 maxmin[0].lat = wpt_maxmin[0].lat;
2299 maxmin[0].lat = trk_maxmin[0].lat;
2301 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2302 maxmin[0].lon = wpt_maxmin[0].lon;
2305 maxmin[0].lon = trk_maxmin[0].lon;
2307 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2308 maxmin[1].lat = wpt_maxmin[1].lat;
2311 maxmin[1].lat = trk_maxmin[1].lat;
2313 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2314 maxmin[1].lon = wpt_maxmin[1].lon;
2317 maxmin[1].lon = trk_maxmin[1].lon;
2321 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2323 /* 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... */
2324 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2325 trw_layer_find_maxmin (vtl, maxmin);
2326 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2330 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2331 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2336 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2339 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2340 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2342 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2345 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2347 /* First set the center [in case previously viewing from elsewhere] */
2348 /* Then loop through zoom levels until provided positions are in view */
2349 /* This method is not particularly fast - but should work well enough */
2350 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2352 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2353 vik_viewport_set_center_coord ( vvp, &coord );
2355 /* Convert into definite 'smallest' and 'largest' positions */
2356 struct LatLon minmin;
2357 if ( maxmin[0].lat < maxmin[1].lat )
2358 minmin.lat = maxmin[0].lat;
2360 minmin.lat = maxmin[1].lat;
2362 struct LatLon maxmax;
2363 if ( maxmin[0].lon > maxmin[1].lon )
2364 maxmax.lon = maxmin[0].lon;
2366 maxmax.lon = maxmin[1].lon;
2368 /* Never zoom in too far - generally not that useful, as too close ! */
2369 /* Always recalculate the 'best' zoom level */
2371 vik_viewport_set_zoom ( vvp, zoom );
2373 gdouble min_lat, max_lat, min_lon, max_lon;
2374 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2375 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2376 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2377 /* NB I think the logic used in this test to determine if the bounds is within view
2378 fails if track goes across 180 degrees longitude.
2379 Hopefully that situation is not too common...
2380 Mind you viking doesn't really do edge locations to well anyway */
2381 if ( min_lat < minmin.lat &&
2382 max_lat > minmin.lat &&
2383 min_lon < maxmax.lon &&
2384 max_lon > maxmax.lon )
2385 /* Found within zoom level */
2390 vik_viewport_set_zoom ( vvp, zoom );
2394 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2396 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2397 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2398 trw_layer_find_maxmin (vtl, maxmin);
2399 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2402 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2407 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2409 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])) ) ) {
2410 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2413 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2416 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2418 GtkWidget *file_selector;
2420 gboolean failed = FALSE;
2421 file_selector = gtk_file_chooser_dialog_new (title,
2423 GTK_FILE_CHOOSER_ACTION_SAVE,
2424 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2425 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2427 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2429 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2431 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2432 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2434 gtk_widget_hide ( file_selector );
2435 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2440 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2442 gtk_widget_hide ( file_selector );
2443 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2448 gtk_widget_destroy ( file_selector );
2450 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2453 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2455 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2458 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2460 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2463 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2465 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2466 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2467 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2468 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2470 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2472 g_free ( auto_save_name );
2475 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2477 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2478 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2479 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2480 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2482 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2484 g_free ( auto_save_name );
2488 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2491 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2493 gchar *name_used = NULL;
2496 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2497 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2499 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2503 gchar *quoted_file = g_shell_quote ( name_used );
2504 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2505 g_free ( quoted_file );
2506 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2508 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2509 g_error_free ( err );
2513 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2514 //g_remove ( name_used );
2515 // Perhaps should be deleted when the program ends?
2516 // For now leave it to the user to delete it / use system temp cleanup methods.
2517 g_free ( name_used );
2521 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2523 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2526 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2528 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2531 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2533 gpointer layer_and_vlp[2];
2534 layer_and_vlp[0] = pass_along[0];
2535 layer_and_vlp[1] = pass_along[1];
2536 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2538 if ( !trk || !trk->name )
2541 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2542 gchar *auto_save_name = g_strdup ( trk->name );
2543 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2544 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2546 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2548 g_free ( auto_save_name );
2552 VikWaypoint *wp; // input
2553 gpointer uuid; // output
2556 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2558 wpu_udata *user_data = udata;
2559 if ( wp == user_data->wp ) {
2560 user_data->uuid = id;
2566 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2568 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2569 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2570 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2572 GTK_RESPONSE_REJECT,
2574 GTK_RESPONSE_ACCEPT,
2577 GtkWidget *label, *entry;
2578 label = gtk_label_new(_("Waypoint Name:"));
2579 entry = gtk_entry_new();
2581 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2582 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2583 gtk_widget_show_all ( label );
2584 gtk_widget_show_all ( entry );
2586 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2588 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2590 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2591 // Find *first* wp with the given name
2592 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2595 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2598 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2599 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2601 // Find and select on the side panel
2606 // Hmmm, want key of it
2607 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2609 if ( wpf && udata.uuid ) {
2610 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2611 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2620 gtk_widget_destroy ( dia );
2623 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2625 gchar *default_name = highest_wp_number_get(vtl);
2626 VikWaypoint *wp = vik_waypoint_new();
2627 gchar *returned_name;
2629 wp->coord = *def_coord;
2631 // Attempt to auto set height if DEM data is available
2632 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2633 if ( elev != VIK_DEM_INVALID_ELEVATION )
2634 wp->altitude = (gdouble)elev;
2636 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2638 if ( returned_name )
2641 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2642 g_free (default_name);
2643 g_free (returned_name);
2646 g_free (default_name);
2647 vik_waypoint_free(wp);
2651 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2654 struct LatLon one_ll, two_ll;
2655 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2657 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2658 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2659 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2660 VikViewport *vvp = vik_window_viewport(vw);
2661 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2662 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2663 vik_coord_to_latlon(&one, &one_ll);
2664 vik_coord_to_latlon(&two, &two_ll);
2665 if (one_ll.lat > two_ll.lat) {
2666 maxmin[0].lat = one_ll.lat;
2667 maxmin[1].lat = two_ll.lat;
2670 maxmin[0].lat = two_ll.lat;
2671 maxmin[1].lat = one_ll.lat;
2673 if (one_ll.lon > two_ll.lon) {
2674 maxmin[0].lon = one_ll.lon;
2675 maxmin[1].lon = two_ll.lon;
2678 maxmin[0].lon = two_ll.lon;
2679 maxmin[1].lon = one_ll.lon;
2681 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2684 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2686 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2687 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2688 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2690 trw_layer_find_maxmin (vtl, maxmin);
2691 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2694 #ifdef VIK_CONFIG_GEOTAG
2695 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2697 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2699 // Update directly - not changing the mtime
2700 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2703 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2705 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2708 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2712 * Use code in separate file for this feature as reasonably complex
2714 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2716 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2717 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2718 // Unset so can be reverified later if necessary
2719 vtl->has_verified_thumbnails = FALSE;
2721 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2727 static void trw_layer_geotagging ( gpointer lav[2] )
2729 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2730 // Unset so can be reverified later if necessary
2731 vtl->has_verified_thumbnails = FALSE;
2733 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2740 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2743 * Acquire into this TRW Layer straight from GPS Device
2745 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2747 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2748 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2749 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2750 VikViewport *vvp = vik_window_viewport(vw);
2752 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2753 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2756 #ifdef VIK_CONFIG_GOOGLE
2758 * Acquire into this TRW Layer from Google Directions
2760 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2762 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2763 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2764 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2765 VikViewport *vvp = vik_window_viewport(vw);
2767 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2771 #ifdef VIK_CONFIG_OPENSTREETMAP
2773 * Acquire into this TRW Layer from OSM
2775 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2777 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2778 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2779 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2780 VikViewport *vvp = vik_window_viewport(vw);
2782 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2786 #ifdef VIK_CONFIG_GEOCACHES
2788 * Acquire into this TRW Layer from Geocaching.com
2790 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2792 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2793 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2794 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2795 VikViewport *vvp = vik_window_viewport(vw);
2797 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2801 #ifdef VIK_CONFIG_GEOTAG
2803 * Acquire into this TRW Layer from images
2805 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2807 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2808 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2809 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2810 VikViewport *vvp = vik_window_viewport(vw);
2812 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2813 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2815 // Reverify thumbnails as they may have changed
2816 vtl->has_verified_thumbnails = FALSE;
2817 trw_layer_verify_thumbnails ( vtl, NULL );
2821 static void trw_layer_gps_upload ( gpointer lav[2] )
2823 gpointer pass_along[6];
2824 pass_along[0] = lav[0];
2825 pass_along[1] = lav[1];
2826 pass_along[2] = NULL; // No track - operate on the layer
2827 pass_along[3] = NULL;
2828 pass_along[4] = NULL;
2829 pass_along[5] = NULL;
2831 trw_layer_gps_upload_any ( pass_along );
2835 * If pass_along[3] is defined that this will upload just that track
2837 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
2839 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2840 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
2842 // May not actually get a track here as pass_along[3] can be null
2843 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2845 gboolean on_track = track ? TRUE : FALSE;
2847 if (on_track && !track->visible) {
2848 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
2852 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
2853 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2854 GTK_DIALOG_DESTROY_WITH_PARENT,
2856 GTK_RESPONSE_ACCEPT,
2858 GTK_RESPONSE_REJECT,
2861 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2862 GtkWidget *response_w = NULL;
2863 #if GTK_CHECK_VERSION (2, 20, 0)
2864 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2868 gtk_widget_grab_focus ( response_w );
2870 gpointer dgs = datasource_gps_setup ( dialog, on_track );
2872 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2873 datasource_gps_clean_up ( dgs );
2874 gtk_widget_destroy ( dialog );
2878 // Get info from reused datasource dialog widgets
2879 gchar* protocol = datasource_gps_get_protocol ( dgs );
2880 gchar* port = datasource_gps_get_descriptor ( dgs );
2881 // NB don't free the above strings as they're references to values held elsewhere
2882 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
2883 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
2884 gboolean turn_off = datasource_gps_get_off ( dgs );
2886 gtk_widget_destroy ( dialog );
2888 // When called from the viewport - work the corresponding layerspanel:
2890 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
2893 // Apply settings to transfer to the GPS device
2900 vik_layers_panel_get_viewport (vlp),
2908 * Acquire into this TRW Layer from any GPS Babel supported file
2910 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
2912 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2913 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2914 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2915 VikViewport *vvp = vik_window_viewport(vw);
2917 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
2920 static void trw_layer_new_wp ( gpointer lav[2] )
2922 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2923 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2924 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2925 instead return true if you want to update. */
2926 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 )
2927 vik_layers_panel_emit_update ( vlp );
2930 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2932 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2933 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2935 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2936 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2937 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2938 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2939 vik_layers_panel_emit_update ( vlp );
2943 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2945 /* NB do not care if wp is visible or not */
2946 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2949 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2951 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2952 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2954 /* Only 1 waypoint - jump straight to it */
2955 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2956 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2957 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2959 /* If at least 2 waypoints - find center and then zoom to fit */
2960 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2962 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2963 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2964 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2967 vik_layers_panel_emit_update ( vlp );
2970 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2972 static gpointer pass_along[2];
2974 GtkWidget *export_submenu;
2975 pass_along[0] = vtl;
2976 pass_along[1] = vlp;
2978 item = gtk_menu_item_new();
2979 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2980 gtk_widget_show ( item );
2982 /* Now with icons */
2983 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2984 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2986 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2987 gtk_widget_show ( item );
2989 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2991 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2992 gtk_widget_show ( item );
2994 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2996 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2997 gtk_widget_show ( item );
2999 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3000 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3001 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3002 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3003 gtk_widget_show ( item );
3005 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3006 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3007 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3008 gtk_widget_show ( item );
3010 export_submenu = gtk_menu_new ();
3011 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3012 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3013 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3014 gtk_widget_show ( item );
3015 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3017 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3019 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3020 gtk_widget_show ( item );
3022 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3024 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3025 gtk_widget_show ( item );
3027 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), 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 _KML...") );
3033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3034 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3035 gtk_widget_show ( item );
3037 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3038 item = gtk_menu_item_new_with_mnemonic ( external1 );
3039 g_free ( external1 );
3040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3041 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3042 gtk_widget_show ( item );
3044 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3045 item = gtk_menu_item_new_with_mnemonic ( external2 );
3046 g_free ( external2 );
3047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3048 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3049 gtk_widget_show ( item );
3051 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3052 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3054 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3055 gtk_widget_show ( item );
3057 #ifdef VIK_CONFIG_GEONAMES
3058 GtkWidget *wikipedia_submenu = gtk_menu_new();
3059 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3061 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3062 gtk_widget_show(item);
3063 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3065 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3068 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3069 gtk_widget_show ( item );
3071 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3072 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3074 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3075 gtk_widget_show ( item );
3078 #ifdef VIK_CONFIG_GEOTAG
3079 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3080 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3081 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3082 gtk_widget_show ( item );
3085 GtkWidget *acquire_submenu = gtk_menu_new ();
3086 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3087 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3088 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3089 gtk_widget_show ( item );
3090 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3092 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3094 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3095 gtk_widget_show ( item );
3097 #ifdef VIK_CONFIG_GOOGLE
3098 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3100 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3101 gtk_widget_show ( item );
3104 #ifdef VIK_CONFIG_OPENSTREETMAP
3105 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3106 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3107 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3108 gtk_widget_show ( item );
3111 #ifdef VIK_CONFIG_GEOCACHES
3112 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3114 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3115 gtk_widget_show ( item );
3118 #ifdef VIK_CONFIG_GEOTAG
3119 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3121 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3122 gtk_widget_show ( item );
3125 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3127 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3128 gtk_widget_show ( item );
3130 GtkWidget *upload_submenu = gtk_menu_new ();
3131 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3133 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3134 gtk_widget_show ( item );
3135 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3137 #ifdef VIK_CONFIG_OPENSTREETMAP
3138 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3141 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3142 gtk_widget_show ( item );
3145 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3146 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3148 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3149 gtk_widget_show ( item );
3151 GtkWidget *delete_submenu = gtk_menu_new ();
3152 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3153 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3154 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3155 gtk_widget_show ( item );
3156 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3158 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3159 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3161 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3162 gtk_widget_show ( item );
3164 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3165 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3166 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3167 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3168 gtk_widget_show ( item );
3170 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3171 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3172 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3173 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3174 gtk_widget_show ( item );
3176 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3179 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3180 gtk_widget_show ( item );
3182 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3183 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3185 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3186 gtk_widget_show ( item );
3189 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3190 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3192 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3193 gtk_widget_show ( item );
3197 // Fake Waypoint UUIDs vi simple increasing integer
3198 static guint wp_uuid = 0;
3200 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3204 vik_waypoint_set_name (wp, name);
3206 if ( VIK_LAYER(vtl)->realized )
3208 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3210 // Visibility column always needed for waypoints
3211 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3212 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 );
3214 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 );
3216 // Actual setting of visibility dependent on the waypoint
3217 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3219 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3222 highest_wp_number_add_wp(vtl, name);
3223 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3227 // Fake Track UUIDs vi simple increasing integer
3228 static guint tr_uuid = 0;
3230 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3234 vik_track_set_name (t, name);
3236 if ( VIK_LAYER(vtl)->realized )
3238 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3239 // Visibility column always needed for tracks
3240 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3241 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 );
3243 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 );
3245 // Actual setting of visibility dependent on the track
3246 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3248 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3251 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3255 /* to be called whenever a track has been deleted or may have been changed. */
3256 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3258 if (vtl->current_tp_track == trk )
3259 trw_layer_cancel_current_tp ( vtl, FALSE );
3262 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3265 gchar *newname = g_strdup(name);
3266 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
3267 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
3268 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3270 newname = new_newname;
3276 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3278 // No more uniqueness of name forced when loading from a file
3279 // This now makes this function a little redunant as we just flow the parameters through
3280 vik_trw_layer_add_waypoint ( vtl, name, wp );
3283 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3285 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3286 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3287 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3288 vik_track_free ( tr );
3289 vtl->route_finder_append = FALSE; /* this means we have added it */
3292 // No more uniqueness of name forced when loading from a file
3293 vik_trw_layer_add_track ( vtl, name, tr );
3295 if ( vtl->route_finder_check_added_track ) {
3296 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3297 vtl->route_finder_added_track = tr;
3302 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3304 *l = g_list_append(*l, id);
3308 * Move an item from one TRW layer to another TRW layer
3310 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3312 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3313 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3315 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3317 VikTrack *trk2 = vik_track_copy ( trk );
3318 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3319 vik_trw_layer_delete_track ( vtl_src, trk );
3322 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3323 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3325 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3327 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3328 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3329 trw_layer_delete_waypoint ( vtl_src, wp );
3333 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3335 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3336 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3338 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3339 GList *items = NULL;
3342 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3343 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3345 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3346 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3351 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3352 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3354 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3361 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3362 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3367 VikTrack *trk; // input
3368 gpointer uuid; // output
3371 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3373 trku_udata *user_data = udata;
3374 if ( trk == user_data->trk ) {
3375 user_data->uuid = id;
3381 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3383 gboolean was_visible = FALSE;
3385 if ( trk && trk->name ) {
3387 if ( trk == vtl->current_track ) {
3388 vtl->current_track = NULL;
3389 vtl->current_tp_track = NULL;
3390 vtl->current_tp_id = NULL;
3391 vtl->moving_tp = FALSE;
3394 was_visible = trk->visible;
3396 if ( trk == vtl->route_finder_current_track )
3397 vtl->route_finder_current_track = NULL;
3399 if ( trk == vtl->route_finder_added_track )
3400 vtl->route_finder_added_track = NULL;
3406 // Hmmm, want key of it
3407 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3409 if ( trkf && udata.uuid ) {
3410 /* could be current_tp, so we have to check */
3411 trw_layer_cancel_tps_of_track ( vtl, trk );
3413 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3416 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3417 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3418 g_hash_table_remove ( vtl->tracks, udata.uuid );
3425 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3427 gboolean was_visible = FALSE;
3429 if ( wp && wp->name ) {
3431 if ( wp == vtl->current_wp ) {
3432 vtl->current_wp = NULL;
3433 vtl->current_wp_id = NULL;
3434 vtl->moving_wp = FALSE;
3437 was_visible = wp->visible;
3443 // Hmmm, want key of it
3444 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3446 if ( wpf && udata.uuid ) {
3447 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3450 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3451 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3453 highest_wp_number_remove_wp(vtl, wp->name);
3454 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3463 // Only for temporary use by trw_layer_delete_waypoint_by_name
3464 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3466 wpu_udata *user_data = udata;
3467 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3468 user_data->uuid = id;
3475 * Delete a waypoint by the given name
3476 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3477 * as there be multiple waypoints with the same name
3479 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3482 // Fake a waypoint with the given name
3483 udata.wp = vik_waypoint_new ();
3484 vik_waypoint_set_name (udata.wp, name);
3485 // Currently only the name is used in this waypoint find function
3488 // Hmmm, want key of it
3489 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3491 vik_waypoint_free (udata.wp);
3493 if ( wpf && udata.uuid )
3494 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3500 VikTrack *trk; // input
3501 gpointer uuid; // output
3504 // Only for temporary use by trw_layer_delete_track_by_name
3505 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3507 tpu_udata *user_data = udata;
3508 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3509 user_data->uuid = id;
3516 * Delete a track by the given name
3517 * NOTE: ATM this will delete the first encountered Track with the specified name
3518 * as there be multiple track with the same name
3520 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3523 // Fake a track with the given name
3524 udata.trk = vik_track_new ();
3525 vik_track_set_name (udata.trk, name);
3526 // Currently only the name is used in this waypoint find function
3529 // Hmmm, want key of it
3530 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3532 vik_track_free (udata.trk);
3534 if ( trkf && udata.uuid )
3535 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3540 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3542 vik_treeview_item_delete (vt, it );
3545 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3548 vtl->current_track = NULL;
3549 vtl->route_finder_current_track = NULL;
3550 vtl->route_finder_added_track = NULL;
3551 if (vtl->current_tp_track)
3552 trw_layer_cancel_current_tp(vtl, FALSE);
3554 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3555 g_hash_table_remove_all(vtl->tracks_iters);
3556 g_hash_table_remove_all(vtl->tracks);
3558 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3561 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3563 vtl->current_wp = NULL;
3564 vtl->current_wp_id = NULL;
3565 vtl->moving_wp = FALSE;
3567 highest_wp_number_reset(vtl);
3569 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3570 g_hash_table_remove_all(vtl->waypoints_iters);
3571 g_hash_table_remove_all(vtl->waypoints);
3573 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3576 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3578 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3579 // Get confirmation from the user
3580 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3581 _("Are you sure you want to delete all tracks in %s?"),
3582 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3583 vik_trw_layer_delete_all_tracks (vtl);
3586 static void trw_layer_delete_all_waypoints ( 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 waypoints in %s?"),
3592 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3593 vik_trw_layer_delete_all_waypoints (vtl);
3596 static void trw_layer_delete_item ( gpointer pass_along[6] )
3598 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3599 gboolean was_visible = FALSE;
3600 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3602 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3603 if ( wp && wp->name ) {
3604 if ( GPOINTER_TO_INT ( pass_along[4]) )
3605 // Get confirmation from the user
3606 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3607 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3608 _("Are you sure you want to delete the waypoint \"%s\""),
3611 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3616 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3617 if ( trk && trk->name ) {
3618 if ( GPOINTER_TO_INT ( pass_along[4]) )
3619 // Get confirmation from the user
3620 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3621 _("Are you sure you want to delete the track \"%s\""),
3624 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3628 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3632 static void trw_layer_properties_item ( gpointer pass_along[7] )
3634 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3635 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3637 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3639 if ( wp && wp->name )
3641 gboolean updated = FALSE;
3642 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3644 if ( updated && VIK_LAYER(vtl)->visible )
3645 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3650 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3651 if ( tr && tr->name )
3653 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3655 pass_along[1], /* vlp */
3656 pass_along[5] ); /* vvp */
3662 Parameter 1 -> VikLayersPanel
3663 Parameter 2 -> VikLayer
3664 Parameter 3 -> VikViewport
3666 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3669 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3670 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3673 /* since vlp not set, vl & vvp should be valid instead! */
3675 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3676 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3681 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3683 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3684 if ( trps && trps->data )
3685 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3688 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3690 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3691 if ( track && track->trackpoints )
3693 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3695 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
3696 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3697 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3698 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3699 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3703 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3705 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3706 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3708 vtl->current_track = track;
3709 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3711 if ( track->trackpoints )
3712 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3715 #ifdef VIK_CONFIG_GOOGLE
3717 * extend a track using route finder
3719 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3721 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3722 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3723 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3725 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3726 vtl->route_finder_coord = last_coord;
3727 vtl->route_finder_current_track = track;
3728 vtl->route_finder_started = TRUE;
3730 if ( track->trackpoints )
3731 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3736 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3738 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3739 /* Also warn if overwrite old elevation data */
3740 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3742 vik_track_apply_dem_data ( track );
3745 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3747 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3750 trps = g_list_last(trps);
3751 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3754 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3756 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3759 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3762 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3764 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3767 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3770 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3772 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3775 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3779 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3781 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3783 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3784 if ( trk && trk->trackpoints )
3786 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3787 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
3788 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3789 if ( pass_along[1] )
3790 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3792 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3796 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3798 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3799 trw_layer_tpwin_init ( vtl );
3802 /*************************************
3803 * merge/split by time routines
3804 *************************************/
3806 /* called for each key in track hash table.
3807 * If the current track has the same time stamp type, add it to the result,
3808 * except the one pointed by "exclude".
3809 * set exclude to NULL if there is no exclude to check.
3810 * Note that the result is in reverse (for performance reasons).
3815 gboolean with_timestamps;
3817 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3819 twt_udata *user_data = udata;
3820 VikTrackpoint *p1, *p2;
3822 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3826 if (VIK_TRACK(value)->trackpoints) {
3827 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3828 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3830 if ( user_data->with_timestamps ) {
3831 if (!p1->has_timestamp || !p2->has_timestamp) {
3836 // Don't add tracks with timestamps when getting non timestamp tracks
3837 if (p1->has_timestamp || p2->has_timestamp) {
3843 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3846 /* called for each key in track hash table. if original track user_data[1] is close enough
3847 * to the passed one, add it to list in user_data[0]
3849 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
3852 VikTrackpoint *p1, *p2;
3853 VikTrack *trk = VIK_TRACK(value);
3855 GList **nearby_tracks = ((gpointer *)user_data)[0];
3856 GList *tpoints = ((gpointer *)user_data)[1];
3859 * detect reasons for not merging, and return
3860 * if no reason is found not to merge, then do it.
3863 // Exclude the original track from the compiled list
3864 if (trk->trackpoints == tpoints) {
3868 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
3869 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
3871 if (trk->trackpoints) {
3872 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
3873 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
3875 if (!p1->has_timestamp || !p2->has_timestamp) {
3876 //g_print("no timestamp\n");
3880 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3881 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
3882 if (! (abs(t1 - p2->timestamp) < threshold ||
3884 abs(p1->timestamp - t2) < threshold)
3891 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
3894 /* comparison function used to sort tracks; a and b are hash table keys */
3895 /* Not actively used - can be restored if needed
3896 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3898 GHashTable *tracks = user_data;
3901 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3902 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3904 if (t1 < t2) return -1;
3905 if (t1 > t2) return 1;
3910 /* comparison function used to sort trackpoints */
3911 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3913 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3915 if (t1 < t2) return -1;
3916 if (t1 > t2) return 1;
3921 * comparison function which can be used to sort tracks or waypoints by name
3923 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3925 const gchar* namea = (const gchar*) a;
3926 const gchar* nameb = (const gchar*) b;
3927 if ( namea == NULL || nameb == NULL)
3930 // Same sort method as used in the vik_treeview_*_alphabetize functions
3931 return strcmp ( namea, nameb );
3935 * Attempt to merge selected track with other tracks specified by the user
3936 * Tracks to merge with must be of the same 'type' as the selected track -
3937 * either all with timestamps, or all without timestamps
3939 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3941 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3942 GList *other_tracks = NULL;
3943 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3945 if ( !track->trackpoints )
3949 udata.result = &other_tracks;
3950 udata.exclude = track->trackpoints;
3951 // Allow merging with 'similar' time type time tracks
3952 // i.e. either those times, or those without
3953 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3955 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3956 other_tracks = g_list_reverse(other_tracks);
3958 if ( !other_tracks ) {
3959 if ( udata.with_timestamps )
3960 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3962 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3966 // Sort alphabetically for user presentation
3967 // Convert into list of names for usage with dialog function
3968 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3969 GList *other_tracks_names = NULL;
3970 GList *iter = g_list_first ( other_tracks );
3972 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
3973 iter = g_list_next ( iter );
3976 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
3978 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3979 other_tracks_names, TRUE,
3980 _("Merge with..."), _("Select track to merge with"));
3981 g_list_free(other_tracks);
3982 g_list_free(other_tracks_names);
3987 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3988 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
3990 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3991 merge_track->trackpoints = NULL;
3992 vik_trw_layer_delete_track (vtl, merge_track);
3993 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3996 /* TODO: free data before free merge_list */
3997 for (l = merge_list; l != NULL; l = g_list_next(l))
3999 g_list_free(merge_list);
4000 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4004 // c.f. trw_layer_sorted_track_id_by_name_list
4005 // but don't add the specified track to the list (normally current track)
4006 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4008 twt_udata *user_data = udata;
4011 if (trk->trackpoints == user_data->exclude) {
4015 // Sort named list alphabetically
4016 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4020 * Join - this allows combining 'routes' and 'tracks'
4021 * i.e. doesn't care about whether tracks have consistent timestamps
4022 * ATM can only append one track at a time to the currently selected track
4024 static void trw_layer_append_track ( gpointer pass_along[6] )
4027 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4028 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4030 GList *other_tracks_names = NULL;
4032 // Sort alphabetically for user presentation
4033 // Convert into list of names for usage with dialog function
4034 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4036 udata.result = &other_tracks_names;
4037 udata.exclude = trk->trackpoints;
4039 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4041 // Note the limit to selecting one track only
4042 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4043 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4044 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4048 _("Select the track to append after the current track"));
4050 g_list_free(other_tracks_names);
4052 // It's a list, but shouldn't contain more than one other track!
4053 if ( append_list ) {
4055 for (l = append_list; l != NULL; l = g_list_next(l)) {
4056 // TODO: at present this uses the first track found by name,
4057 // which with potential multiple same named tracks may not be the one selected...
4058 VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data );
4059 if ( append_track ) {
4060 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4061 append_track->trackpoints = NULL;
4062 vik_trw_layer_delete_track (vtl, append_track);
4065 for (l = append_list; l != NULL; l = g_list_next(l))
4067 g_list_free(append_list);
4068 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4072 /* merge by segments */
4073 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4075 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4076 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4077 guint segments = vik_track_merge_segments ( trk );
4078 // NB currently no need to redraw as segments not actually shown on the display
4079 // However inform the user of what happened:
4081 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4082 g_snprintf(str, 64, tmp_str, segments);
4083 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4086 /* merge by time routine */
4087 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4089 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4093 GList *tracks_with_timestamp = NULL;
4094 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4095 if (orig_trk->trackpoints &&
4096 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4097 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4102 udata.result = &tracks_with_timestamp;
4103 udata.exclude = orig_trk->trackpoints;
4104 udata.with_timestamps = TRUE;
4105 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4106 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4108 if (!tracks_with_timestamp) {
4109 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4112 g_list_free(tracks_with_timestamp);
4114 static guint threshold_in_minutes = 1;
4115 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4116 _("Merge Threshold..."),
4117 _("Merge when time between tracks less than:"),
4118 &threshold_in_minutes)) {
4122 // keep attempting to merge all tracks until no merges within the time specified is possible
4123 gboolean attempt_merge = TRUE;
4124 GList *nearby_tracks = NULL;
4126 static gpointer params[3];
4128 while ( attempt_merge ) {
4130 // Don't try again unless tracks have changed
4131 attempt_merge = FALSE;
4133 trps = orig_trk->trackpoints;
4137 if (nearby_tracks) {
4138 g_list_free(nearby_tracks);
4139 nearby_tracks = NULL;
4142 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4143 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4145 /* g_print("Original track times: %d and %d\n", t1, t2); */
4146 params[0] = &nearby_tracks;
4147 params[1] = (gpointer)trps;
4148 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4150 /* get a list of adjacent-in-time tracks */
4151 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4154 GList *l = nearby_tracks;
4157 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4158 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4160 t1 = get_first_trackpoint(l)->timestamp;
4161 t2 = get_last_trackpoint(l)->timestamp;
4162 #undef get_first_trackpoint
4163 #undef get_last_trackpoint
4164 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4167 /* remove trackpoints from merged track, delete track */
4168 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4169 VIK_TRACK(l->data)->trackpoints = NULL;
4170 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4172 // Tracks have changed, therefore retry again against all the remaining tracks
4173 attempt_merge = TRUE;
4178 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
4181 g_list_free(nearby_tracks);
4182 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4186 * Split a track at the currently selected trackpoint
4188 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl )
4190 if ( !vtl->current_tpl )
4193 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
4194 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4196 VikTrack *tr = vik_track_new ();
4197 GList *newglist = g_list_alloc ();
4198 newglist->prev = NULL;
4199 newglist->next = vtl->current_tpl->next;
4200 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4201 tr->trackpoints = newglist;
4203 vtl->current_tpl->next->prev = newglist; /* end old track here */
4204 vtl->current_tpl->next = NULL;
4206 vtl->current_tpl = newglist; /* change tp to first of new track. */
4207 vtl->current_tp_track = tr;
4211 vik_trw_layer_add_track ( vtl, name, tr );
4217 // Also need id of newly created track
4218 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4219 if ( trkf && udata.uuid )
4220 vtl->current_tp_id = udata.uuid;
4222 vtl->current_tp_id = NULL;
4224 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4229 /* split by time routine */
4230 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
4232 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4233 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4234 GList *trps = track->trackpoints;
4236 GList *newlists = NULL;
4237 GList *newtps = NULL;
4238 static guint thr = 1;
4245 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4246 _("Split Threshold..."),
4247 _("Split when time between trackpoints exceeds:"),
4252 /* iterate through trackpoints, and copy them into new lists without touching original list */
4253 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
4257 ts = VIK_TRACKPOINT(iter->data)->timestamp;
4259 g_print("panic: ts < prev_ts: this should never happen!\n");
4262 if (ts - prev_ts > thr*60) {
4263 /* flush accumulated trackpoints into new list */
4264 newlists = g_list_append(newlists, g_list_reverse(newtps));
4268 /* accumulate trackpoint copies in newtps, in reverse order */
4269 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4271 iter = g_list_next(iter);
4274 newlists = g_list_append(newlists, g_list_reverse(newtps));
4277 /* put lists of trackpoints into tracks */
4279 // Only bother updating if the split results in new tracks
4280 if (g_list_length (newlists) > 1) {
4285 tr = vik_track_new();
4286 tr->visible = track->visible;
4287 tr->trackpoints = (GList *)(iter->data);
4289 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4290 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4291 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
4292 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
4294 iter = g_list_next(iter);
4296 // Remove original track and then update the display
4297 vik_trw_layer_delete_track (vtl, track);
4298 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4300 g_list_free(newlists);
4304 * Split a track by the number of points as specified by the user
4306 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
4308 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4309 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4311 // Check valid track
4312 GList *trps = track->trackpoints;
4316 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4317 _("Split Every Nth Point"),
4318 _("Split on every Nth point:"),
4319 250, // Default value as per typical limited track capacity of various GPS devices
4323 // Was a valid number returned?
4329 GList *newlists = NULL;
4330 GList *newtps = NULL;
4335 /* accumulate trackpoint copies in newtps, in reverse order */
4336 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4338 if (count >= points) {
4339 /* flush accumulated trackpoints into new list */
4340 newlists = g_list_append(newlists, g_list_reverse(newtps));
4344 iter = g_list_next(iter);
4347 // If there is a remaining chunk put that into the new split list
4348 // This may well be the whole track if no split points were encountered
4350 newlists = g_list_append(newlists, g_list_reverse(newtps));
4353 /* put lists of trackpoints into tracks */
4355 // Only bother updating if the split results in new tracks
4356 if (g_list_length (newlists) > 1) {
4361 tr = vik_track_new();
4362 tr->visible = track->visible;
4363 tr->trackpoints = (GList *)(iter->data);
4365 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4366 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4368 iter = g_list_next(iter);
4370 // Remove original track and then update the display
4371 vik_trw_layer_delete_track (vtl, track);
4372 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4374 g_list_free(newlists);
4378 * Split a track at the currently selected trackpoint
4380 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
4382 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4383 trw_layer_split_at_selected_trackpoint ( vtl );
4387 * Split a track by its segments
4389 static void trw_layer_split_segments ( gpointer pass_along[6] )
4391 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4392 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4395 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
4398 for ( i = 0; i < ntracks; i++ ) {
4400 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
4401 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
4406 // Remove original track
4407 vik_trw_layer_delete_track ( vtl, trk );
4408 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4411 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
4414 /* end of split/merge routines */
4417 * Delete adjacent track points at the same position
4418 * AKA Delete Dulplicates on the Properties Window
4420 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
4422 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4423 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4425 gulong removed = vik_track_remove_dup_points ( trk );
4427 // Track has been updated so update tps:
4428 trw_layer_cancel_tps_of_track ( vtl, trk );
4430 // Inform user how much was deleted as it's not obvious from the normal view
4432 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4433 g_snprintf(str, 64, tmp_str, removed);
4434 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4436 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4440 * Delete adjacent track points with the same timestamp
4441 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
4443 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
4445 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4446 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4448 gulong removed = vik_track_remove_same_time_points ( trk );
4450 // Track has been updated so update tps:
4451 trw_layer_cancel_tps_of_track ( vtl, trk );
4453 // Inform user how much was deleted as it's not obvious from the normal view
4455 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4456 g_snprintf(str, 64, tmp_str, removed);
4457 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4459 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4465 static void trw_layer_reverse ( gpointer pass_along[6] )
4467 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4468 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4470 // Check valid track
4471 GList *trps = track->trackpoints;
4475 vik_track_reverse ( track );
4477 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4481 * Similar to trw_layer_enum_item, but this uses a sorted method
4484 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4486 GList **list = (GList**)udata;
4487 // *list = g_list_prepend(*all, key); //unsorted method
4488 // Sort named list alphabetically
4489 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4494 * Now Waypoint specific sort
4496 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4498 GList **list = (GList**)udata;
4499 // Sort named list alphabetically
4500 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4504 * Track specific sort
4506 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4508 GList **list = (GList**)udata;
4509 // Sort named list alphabetically
4510 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4515 gboolean has_same_track_name;
4516 const gchar *same_track_name;
4517 } same_track_name_udata;
4519 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4521 const gchar* namea = (const gchar*) aa;
4522 const gchar* nameb = (const gchar*) bb;
4525 gint result = strcmp ( namea, nameb );
4527 if ( result == 0 ) {
4528 // Found two names the same
4529 same_track_name_udata *user_data = udata;
4530 user_data->has_same_track_name = TRUE;
4531 user_data->same_track_name = namea;
4534 // Leave ordering the same
4539 * Find out if any tracks have the same name in this layer
4541 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4543 // Sort items by name, then compare if any next to each other are the same
4545 GList *track_names = NULL;
4546 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4549 if ( ! track_names )
4552 same_track_name_udata udata;
4553 udata.has_same_track_name = FALSE;
4555 // Use sort routine to traverse list comparing items
4556 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4557 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4558 // Still no tracks...
4562 return udata.has_same_track_name;
4566 * Force unqiue track names for this layer
4567 * Note the panel is a required parameter to enable the update of the names displayed
4569 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4571 // . Search list for an instance of repeated name
4572 // . get track of this name
4573 // . create new name
4574 // . rename track & update equiv. treeview iter
4575 // . repeat until all different
4577 same_track_name_udata udata;
4579 GList *track_names = NULL;
4580 udata.has_same_track_name = FALSE;
4581 udata.same_track_name = NULL;
4583 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4586 if ( ! track_names )
4589 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4591 // Still no tracks...
4592 if ( ! dummy_list1 )
4595 while ( udata.has_same_track_name ) {
4597 // Find a track with the same name
4598 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4602 g_critical("Houston, we've had a problem.");
4603 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4604 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4609 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4610 vik_track_set_name ( trk, newname );
4616 // Need want key of it for treeview update
4617 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4619 if ( trkf && udataU.uuid ) {
4621 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4624 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4625 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4626 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4631 // Start trying to find same names again...
4633 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4634 udata.has_same_track_name = FALSE;
4635 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4637 // No tracks any more - give up searching
4638 if ( ! dummy_list2 )
4639 udata.has_same_track_name = FALSE;
4643 vik_layers_panel_emit_update ( vlp );
4649 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4651 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4654 // Ensure list of track names offered is unique
4655 if ( trw_layer_has_same_track_names ( vtl ) ) {
4656 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4657 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4658 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4664 // Sort list alphabetically for better presentation
4665 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4668 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4672 // Get list of items to delete from the user
4673 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4676 _("Delete Selection"),
4677 _("Select tracks to delete"));
4680 // Delete requested tracks
4681 // since specificly requested, IMHO no need for extra confirmation
4682 if ( delete_list ) {
4684 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4685 // This deletes first trk it finds of that name (but uniqueness is enforced above)
4686 trw_layer_delete_track_by_name (vtl, l->data);
4688 g_list_free(delete_list);
4689 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4694 gboolean has_same_waypoint_name;
4695 const gchar *same_waypoint_name;
4696 } same_waypoint_name_udata;
4698 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4700 const gchar* namea = (const gchar*) aa;
4701 const gchar* nameb = (const gchar*) bb;
4704 gint result = strcmp ( namea, nameb );
4706 if ( result == 0 ) {
4707 // Found two names the same
4708 same_waypoint_name_udata *user_data = udata;
4709 user_data->has_same_waypoint_name = TRUE;
4710 user_data->same_waypoint_name = namea;
4713 // Leave ordering the same
4718 * Find out if any waypoints have the same name in this layer
4720 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
4722 // Sort items by name, then compare if any next to each other are the same
4724 GList *waypoint_names = NULL;
4725 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4728 if ( ! waypoint_names )
4731 same_waypoint_name_udata udata;
4732 udata.has_same_waypoint_name = FALSE;
4734 // Use sort routine to traverse list comparing items
4735 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4736 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4737 // Still no waypoints...
4741 return udata.has_same_waypoint_name;
4745 * Force unqiue waypoint names for this layer
4746 * Note the panel is a required parameter to enable the update of the names displayed
4748 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4750 // . Search list for an instance of repeated name
4751 // . get waypoint of this name
4752 // . create new name
4753 // . rename waypoint & update equiv. treeview iter
4754 // . repeat until all different
4756 same_waypoint_name_udata udata;
4758 GList *waypoint_names = NULL;
4759 udata.has_same_waypoint_name = FALSE;
4760 udata.same_waypoint_name = NULL;
4762 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4765 if ( ! waypoint_names )
4768 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4770 // Still no waypoints...
4771 if ( ! dummy_list1 )
4774 while ( udata.has_same_waypoint_name ) {
4776 // Find a waypoint with the same name
4777 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
4781 g_critical("Houston, we've had a problem.");
4782 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4783 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
4788 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
4789 vik_waypoint_set_name ( waypoint, newname );
4792 udataU.wp = waypoint;
4795 // Need want key of it for treeview update
4796 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4798 if ( wpf && udataU.uuid ) {
4800 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4803 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4804 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4805 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4810 // Start trying to find same names again...
4811 waypoint_names = NULL;
4812 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4813 udata.has_same_waypoint_name = FALSE;
4814 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4816 // No waypoints any more - give up searching
4817 if ( ! dummy_list2 )
4818 udata.has_same_waypoint_name = FALSE;
4822 vik_layers_panel_emit_update ( vlp );
4828 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4830 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4833 // Ensure list of waypoint names offered is unique
4834 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
4835 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4836 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4837 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4843 // Sort list alphabetically for better presentation
4844 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4846 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4850 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4852 // Get list of items to delete from the user
4853 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4856 _("Delete Selection"),
4857 _("Select waypoints to delete"));
4860 // Delete requested waypoints
4861 // since specificly requested, IMHO no need for extra confirmation
4862 if ( delete_list ) {
4864 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4865 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
4866 trw_layer_delete_waypoint_by_name (vtl, l->data);
4868 g_list_free(delete_list);
4869 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4874 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4876 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4878 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4881 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4883 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4884 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4888 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4890 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4892 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
4894 // No actual change to the name supplied
4895 if (strcmp(newname, wp->name) == 0 )
4898 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4901 // An existing waypoint has been found with the requested name
4902 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4903 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
4908 // Update WP name and refresh the treeview
4909 vik_waypoint_set_name (wp, newname);
4911 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4912 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4915 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4920 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4922 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
4924 // No actual change to the name supplied
4925 if (strcmp(newname, trk->name) == 0)
4928 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4931 // An existing track has been found with the requested name
4932 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4933 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4937 // Update track name and refresh GUI parts
4938 vik_track_set_name (trk, newname);
4940 // Update any subwindows that could be displaying this track which has changed name
4941 // Only one Track Edit Window
4942 if ( l->current_tp_track == trk && l->tpwin ) {
4943 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
4945 // Property Dialog of the track
4946 vik_trw_layer_propwin_update ( trk );
4948 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4949 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4952 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4959 static gboolean is_valid_geocache_name ( gchar *str )
4961 gint len = strlen ( str );
4962 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]));
4965 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
4967 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4968 a_acquire_set_filter_track ( trk );
4971 #ifdef VIK_CONFIG_GOOGLE
4972 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
4974 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
4975 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
4978 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
4980 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4982 gchar *escaped = uri_escape ( tr->comment );
4983 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4984 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4991 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4992 /* viewpoint is now available instead */
4993 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4995 static gpointer pass_along[8];
4997 gboolean rv = FALSE;
5000 pass_along[1] = vlp;
5001 pass_along[2] = GINT_TO_POINTER (subtype);
5002 pass_along[3] = sublayer;
5003 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5004 pass_along[5] = vvp;
5005 pass_along[6] = iter;
5006 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
5008 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5012 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5013 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5014 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5015 gtk_widget_show ( item );
5017 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5018 VikTrwLayer *vtl = l;
5019 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
5020 if (tr && tr->property_dialog)
5021 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5024 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5026 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5027 gtk_widget_show ( item );
5029 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
5030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
5031 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5032 gtk_widget_show ( item );
5034 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
5035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
5036 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5037 gtk_widget_show ( item );
5039 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5041 gboolean separator_created = FALSE;
5043 /* could be a right-click using the tool */
5044 if ( vlp != NULL ) {
5045 item = gtk_menu_item_new ();
5046 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5047 gtk_widget_show ( item );
5049 separator_created = TRUE;
5051 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5052 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
5054 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5055 gtk_widget_show ( item );
5058 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5060 if ( wp && wp->name ) {
5061 if ( is_valid_geocache_name ( wp->name ) ) {
5063 if ( !separator_created ) {
5064 item = gtk_menu_item_new ();
5065 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5066 gtk_widget_show ( item );
5067 separator_created = TRUE;
5070 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
5071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
5072 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5073 gtk_widget_show ( item );
5077 if ( wp && wp->image )
5079 if ( !separator_created ) {
5080 item = gtk_menu_item_new ();
5081 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5082 gtk_widget_show ( item );
5083 separator_created = TRUE;
5086 // Set up image paramater
5087 pass_along[5] = wp->image;
5089 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
5090 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
5091 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
5092 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5093 gtk_widget_show ( item );
5095 #ifdef VIK_CONFIG_GEOTAG
5096 GtkWidget *geotag_submenu = gtk_menu_new ();
5097 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
5098 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
5099 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5100 gtk_widget_show ( item );
5101 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
5103 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
5104 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
5105 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5106 gtk_widget_show ( item );
5108 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
5109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
5110 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5111 gtk_widget_show ( item );
5118 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
5121 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
5122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
5124 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5125 gtk_widget_show ( item );
5128 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
5130 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
5131 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5132 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
5133 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5134 gtk_widget_show ( item );
5136 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
5137 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
5139 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5140 gtk_widget_show ( item );
5142 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
5143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
5145 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5146 gtk_widget_show ( item );
5148 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
5149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
5151 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5152 gtk_widget_show ( item );
5155 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
5159 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
5160 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5161 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
5162 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5163 gtk_widget_show ( item );
5165 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
5166 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5167 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
5168 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5169 gtk_widget_show ( item );
5171 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
5172 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
5174 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5175 gtk_widget_show ( item );
5178 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5180 item = gtk_menu_item_new ();
5181 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5182 gtk_widget_show ( item );
5184 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
5185 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
5187 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5188 gtk_widget_show ( item );
5190 GtkWidget *goto_submenu;
5191 goto_submenu = gtk_menu_new ();
5192 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5193 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5194 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5195 gtk_widget_show ( item );
5196 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
5198 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
5199 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
5200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
5201 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5202 gtk_widget_show ( item );
5204 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
5205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
5207 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5208 gtk_widget_show ( item );
5210 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
5211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
5212 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
5213 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5214 gtk_widget_show ( item );
5216 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
5217 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
5218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
5219 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5220 gtk_widget_show ( item );
5222 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
5223 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
5224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
5225 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5226 gtk_widget_show ( item );
5228 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
5229 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
5230 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
5231 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5232 gtk_widget_show ( item );
5234 GtkWidget *combine_submenu;
5235 combine_submenu = gtk_menu_new ();
5236 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
5237 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
5238 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5239 gtk_widget_show ( item );
5240 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
5242 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
5243 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
5244 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5245 gtk_widget_show ( item );
5247 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
5248 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
5249 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5250 gtk_widget_show ( item );
5252 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
5253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
5254 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5255 gtk_widget_show ( item );
5257 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
5258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
5259 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5260 gtk_widget_show ( item );
5262 GtkWidget *split_submenu;
5263 split_submenu = gtk_menu_new ();
5264 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
5265 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
5266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5267 gtk_widget_show ( item );
5268 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
5270 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
5271 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
5272 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5273 gtk_widget_show ( item );
5275 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
5276 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
5277 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5278 gtk_widget_show ( item );
5280 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
5281 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
5282 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
5283 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5284 gtk_widget_show ( item );
5286 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
5287 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
5288 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5289 gtk_widget_show ( item );
5290 // Make it available only when a trackpoint is selected.
5291 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
5293 GtkWidget *delete_submenu;
5294 delete_submenu = gtk_menu_new ();
5295 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
5296 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
5297 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5298 gtk_widget_show ( item );
5299 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
5301 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
5302 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
5303 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5304 gtk_widget_show ( item );
5306 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
5307 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
5308 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5309 gtk_widget_show ( item );
5311 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
5312 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
5313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
5314 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5315 gtk_widget_show ( item );
5317 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
5319 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
5320 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
5321 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
5322 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5323 gtk_widget_show ( item );
5326 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
5327 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
5328 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
5329 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5330 gtk_widget_show ( item );
5332 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
5333 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
5334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
5335 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5336 gtk_widget_show ( item );
5338 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
5339 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
5340 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
5341 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5342 gtk_widget_show ( item );
5344 #ifdef VIK_CONFIG_GOOGLE
5345 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
5346 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
5347 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
5348 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5349 gtk_widget_show ( item );
5352 GtkWidget *upload_submenu = gtk_menu_new ();
5353 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
5354 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5355 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5356 gtk_widget_show ( item );
5357 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
5359 #ifdef VIK_CONFIG_OPENSTREETMAP
5360 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
5361 // Convert internal pointer into actual track for usage outside this file
5362 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
5363 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5364 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
5365 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5366 gtk_widget_show ( item );
5369 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
5370 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
5371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
5372 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5373 gtk_widget_show ( item );
5375 #ifdef VIK_CONFIG_GOOGLE
5376 if ( is_valid_google_route ( l, sublayer ) )
5378 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
5379 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
5380 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
5381 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5382 gtk_widget_show ( item );
5386 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
5387 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5388 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
5389 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5390 gtk_widget_show ( item );
5392 /* ATM This function is only available via the layers panel, due to needing a vlp */
5394 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
5395 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
5396 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
5398 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5399 gtk_widget_show ( item );
5403 #ifdef VIK_CONFIG_GEOTAG
5404 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
5405 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
5406 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5407 gtk_widget_show ( item );
5410 // Only show on viewport popmenu when a trackpoint is selected
5411 if ( ! vlp && l->current_tpl ) {
5413 item = gtk_menu_item_new ();
5414 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5415 gtk_widget_show ( item );
5417 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
5418 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
5419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
5420 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5421 gtk_widget_show ( item );
5429 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
5432 if (!vtl->current_tpl)
5434 if (!vtl->current_tpl->next)
5437 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
5438 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
5440 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
5443 VikTrackpoint *tp_new = vik_trackpoint_new();
5444 struct LatLon ll_current, ll_next;
5445 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
5446 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
5448 /* main positional interpolation */
5449 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
5450 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
5452 /* Now other properties that can be interpolated */
5453 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
5455 if (tp_current->has_timestamp && tp_next->has_timestamp) {
5456 /* Note here the division is applied to each part, then added
5457 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
5458 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
5459 tp_new->has_timestamp = TRUE;
5462 if (tp_current->speed != NAN && tp_next->speed != NAN)
5463 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
5465 /* TODO - improve interpolation of course, as it may not be correct.
5466 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
5467 [similar applies if value is in radians] */
5468 if (tp_current->course != NAN && tp_next->course != NAN)
5469 tp_new->speed = (tp_current->course + tp_next->course)/2;
5471 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
5473 /* Insert new point into the trackpoints list after the current TP */
5474 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5475 gint index = g_list_index ( tr->trackpoints, tp_current );
5477 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
5482 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
5488 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
5492 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
5494 if ( vtl->current_tpl )
5496 vtl->current_tpl = NULL;
5497 vtl->current_tp_track = NULL;
5498 vtl->current_tp_id = NULL;
5499 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5503 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
5505 g_assert ( vtl->tpwin != NULL );
5506 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
5507 trw_layer_cancel_current_tp ( vtl, TRUE );
5509 if ( vtl->current_tpl == NULL )
5512 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
5514 trw_layer_split_at_selected_trackpoint ( vtl );
5515 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5517 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5519 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5525 // Find available adjacent trackpoint
5526 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5528 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5529 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5531 // Delete current trackpoint
5532 vik_trackpoint_free ( vtl->current_tpl->data );
5533 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5535 // Set to current to the available adjacent trackpoint
5536 vtl->current_tpl = new_tpl;
5538 // Reset dialog with the available adjacent trackpoint
5539 if ( vtl->current_tp_track )
5540 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
5542 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5546 // Delete current trackpoint
5547 vik_trackpoint_free ( vtl->current_tpl->data );
5548 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5549 trw_layer_cancel_current_tp ( vtl, FALSE );
5552 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5554 if ( vtl->current_tp_track )
5555 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
5556 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
5558 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5560 if ( vtl->current_tp_track )
5561 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
5562 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5564 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5566 trw_layer_insert_tp_after_current_tp ( vtl );
5567 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5569 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
5570 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5573 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5577 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5578 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5579 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5580 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5581 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5583 if ( vtl->current_tpl )
5584 if ( vtl->current_tp_track )
5585 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5586 /* set layer name and TP data */
5589 /***************************************************************************
5591 ***************************************************************************/
5593 /*** Utility data structures and functions ****/
5597 gint closest_x, closest_y;
5598 gpointer *closest_wp_id;
5599 VikWaypoint *closest_wp;
5605 gint closest_x, closest_y;
5606 gpointer closest_track_id;
5607 VikTrackpoint *closest_tp;
5612 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5618 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5620 // If waypoint has an image then use the image size to select
5622 gint slackx, slacky;
5623 slackx = wp->image_width / 2;
5624 slacky = wp->image_height / 2;
5626 if ( x <= params->x + slackx && x >= params->x - slackx
5627 && y <= params->y + slacky && y >= params->y - slacky ) {
5628 params->closest_wp_id = id;
5629 params->closest_wp = wp;
5630 params->closest_x = x;
5631 params->closest_y = y;
5634 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5635 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5636 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5638 params->closest_wp_id = id;
5639 params->closest_wp = wp;
5640 params->closest_x = x;
5641 params->closest_y = y;
5645 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5647 GList *tpl = t->trackpoints;
5656 tp = VIK_TRACKPOINT(tpl->data);
5658 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5660 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5661 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5662 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5664 params->closest_track_id = id;
5665 params->closest_tp = tp;
5666 params->closest_tpl = tpl;
5667 params->closest_x = x;
5668 params->closest_y = y;
5674 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5676 TPSearchParams params;
5680 params.closest_track_id = NULL;
5681 params.closest_tp = NULL;
5682 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5683 return params.closest_tp;
5686 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5688 WPSearchParams params;
5692 params.closest_wp = NULL;
5693 params.closest_wp_id = NULL;
5694 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5695 return params.closest_wp;
5699 // Some forward declarations
5700 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5701 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5702 static void marker_end_move ( tool_ed_t *t );
5705 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5709 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5711 // Here always allow snapping back to the original location
5712 // this is useful when one decides not to move the thing afterall
5713 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5716 if ( event->state & GDK_CONTROL_MASK )
5718 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5720 new_coord = tp->coord;
5724 if ( event->state & GDK_SHIFT_MASK )
5726 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5728 new_coord = wp->coord;
5732 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5734 marker_moveto ( t, x, y );
5741 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5743 if ( t->holding && event->button == 1 )
5746 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5749 if ( event->state & GDK_CONTROL_MASK )
5751 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5753 new_coord = tp->coord;
5757 if ( event->state & GDK_SHIFT_MASK )
5759 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5761 new_coord = wp->coord;
5764 marker_end_move ( t );
5766 // Determine if working on a waypoint or a trackpoint
5767 if ( t->is_waypoint )
5768 vtl->current_wp->coord = new_coord;
5770 if ( vtl->current_tpl ) {
5771 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5774 if ( vtl->current_tp_track )
5775 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5780 vtl->current_wp = NULL;
5781 vtl->current_wp_id = NULL;
5782 trw_layer_cancel_current_tp ( vtl, FALSE );
5784 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5791 Returns true if a waypoint or track is found near the requested event position for this particular layer
5792 The item found is automatically selected
5793 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5795 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5797 if ( event->button != 1 )
5800 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5803 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5806 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5808 if (vtl->waypoints_visible) {
5809 WPSearchParams wp_params;
5810 wp_params.vvp = vvp;
5811 wp_params.x = event->x;
5812 wp_params.y = event->y;
5813 wp_params.closest_wp_id = NULL;
5814 wp_params.closest_wp = NULL;
5816 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5818 if ( wp_params.closest_wp ) {
5821 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5823 // Too easy to move it so must be holding shift to start immediately moving it
5824 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5825 if ( event->state & GDK_SHIFT_MASK ||
5826 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5827 // Put into 'move buffer'
5828 // NB vvp & vw already set in tet
5829 tet->vtl = (gpointer)vtl;
5830 tet->is_waypoint = TRUE;
5832 marker_begin_move (tet, event->x, event->y);
5835 vtl->current_wp = wp_params.closest_wp;
5836 vtl->current_wp_id = wp_params.closest_wp_id;
5838 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5844 if (vtl->tracks_visible) {
5845 TPSearchParams tp_params;
5846 tp_params.vvp = vvp;
5847 tp_params.x = event->x;
5848 tp_params.y = event->y;
5849 tp_params.closest_track_id = NULL;
5850 tp_params.closest_tp = NULL;
5852 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5854 if ( tp_params.closest_tp ) {
5856 // Always select + highlight the track
5857 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5859 tet->is_waypoint = FALSE;
5861 // Select the Trackpoint
5862 // Can move it immediately when control held or it's the previously selected tp
5863 if ( event->state & GDK_CONTROL_MASK ||
5864 vtl->current_tpl == tp_params.closest_tpl ) {
5865 // Put into 'move buffer'
5866 // NB vvp & vw already set in tet
5867 tet->vtl = (gpointer)vtl;
5868 marker_begin_move (tet, event->x, event->y);
5871 vtl->current_tpl = tp_params.closest_tpl;
5872 vtl->current_tp_id = tp_params.closest_track_id;
5873 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5875 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5878 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5880 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5885 /* these aren't the droids you're looking for */
5886 vtl->current_wp = NULL;
5887 vtl->current_wp_id = NULL;
5888 trw_layer_cancel_current_tp ( vtl, FALSE );
5891 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5896 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5898 if ( event->button != 3 )
5901 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5904 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5907 /* Post menu for the currently selected item */
5909 /* See if a track is selected */
5910 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5911 if ( track && track->visible ) {
5913 if ( track->name ) {
5915 if ( vtl->track_right_click_menu )
5916 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5918 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5924 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5926 if ( trkf && udataU.uuid ) {
5928 GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5930 trw_layer_sublayer_add_menu_items ( vtl,
5931 vtl->track_right_click_menu,
5933 VIK_TRW_LAYER_SUBLAYER_TRACK,
5939 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5945 /* See if a waypoint is selected */
5946 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5947 if ( waypoint && waypoint->visible ) {
5948 if ( waypoint->name ) {
5950 if ( vtl->wp_right_click_menu )
5951 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
5953 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5956 udata.wp = waypoint;
5959 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
5961 if ( wpf && udata.uuid ) {
5962 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
5964 trw_layer_sublayer_add_menu_items ( vtl,
5965 vtl->wp_right_click_menu,
5967 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5972 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5981 /* background drawing hook, to be passed the viewport */
5982 static gboolean tool_sync_done = TRUE;
5984 static gboolean tool_sync(gpointer data)
5986 VikViewport *vvp = data;
5987 gdk_threads_enter();
5988 vik_viewport_sync(vvp);
5989 tool_sync_done = TRUE;
5990 gdk_threads_leave();
5994 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
5997 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
5998 gdk_gc_set_function ( t->gc, GDK_INVERT );
5999 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6000 vik_viewport_sync(t->vvp);
6005 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
6007 VikViewport *vvp = t->vvp;
6008 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6009 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6013 if (tool_sync_done) {
6014 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
6015 tool_sync_done = FALSE;
6019 static void marker_end_move ( tool_ed_t *t )
6021 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6022 g_object_unref ( t->gc );
6026 /*** Edit waypoint ****/
6028 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6030 tool_ed_t *t = g_new(tool_ed_t, 1);
6036 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6038 WPSearchParams params;
6039 tool_ed_t *t = data;
6040 VikViewport *vvp = t->vvp;
6042 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6049 if ( !vtl->vl.visible || !vtl->waypoints_visible )
6052 if ( vtl->current_wp && vtl->current_wp->visible )
6054 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
6056 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
6058 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
6059 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
6061 if ( event->button == 3 )
6062 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6064 marker_begin_move(t, event->x, event->y);
6071 params.x = event->x;
6072 params.y = event->y;
6073 params.closest_wp_id = NULL;
6074 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6075 params.closest_wp = NULL;
6076 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6077 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
6079 // how do we get here?
6080 marker_begin_move(t, event->x, event->y);
6081 g_critical("shouldn't be here");
6084 else if ( params.closest_wp )
6086 if ( event->button == 3 )
6087 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6089 vtl->waypoint_rightclick = FALSE;
6091 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
6093 vtl->current_wp = params.closest_wp;
6094 vtl->current_wp_id = params.closest_wp_id;
6096 /* could make it so don't update if old WP is off screen and new is null but oh well */
6097 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6101 vtl->current_wp = NULL;
6102 vtl->current_wp_id = NULL;
6103 vtl->waypoint_rightclick = FALSE;
6104 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6108 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6110 tool_ed_t *t = data;
6111 VikViewport *vvp = t->vvp;
6113 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6118 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6121 if ( event->state & GDK_CONTROL_MASK )
6123 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6125 new_coord = tp->coord;
6129 if ( event->state & GDK_SHIFT_MASK )
6131 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6132 if ( wp && wp != vtl->current_wp )
6133 new_coord = wp->coord;
6138 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6140 marker_moveto ( t, x, y );
6147 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6149 tool_ed_t *t = data;
6150 VikViewport *vvp = t->vvp;
6152 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6155 if ( t->holding && event->button == 1 )
6158 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6161 if ( event->state & GDK_CONTROL_MASK )
6163 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6165 new_coord = tp->coord;
6169 if ( event->state & GDK_SHIFT_MASK )
6171 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6172 if ( wp && wp != vtl->current_wp )
6173 new_coord = wp->coord;
6176 marker_end_move ( t );
6178 vtl->current_wp->coord = new_coord;
6179 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6182 /* PUT IN RIGHT PLACE!!! */
6183 if ( event->button == 3 && vtl->waypoint_rightclick )
6185 if ( vtl->wp_right_click_menu )
6186 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
6187 if ( vtl->current_wp ) {
6188 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
6189 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 );
6190 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6192 vtl->waypoint_rightclick = FALSE;
6197 /**** Begin track ***/
6198 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
6203 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6205 vtl->current_track = NULL;
6206 return tool_new_track_click ( vtl, event, vvp );
6209 /*** New track ****/
6211 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
6218 GdkDrawable *drawable;
6224 * Draw specified pixmap
6226 static gboolean draw_sync ( gpointer data )
6228 draw_sync_t *ds = (draw_sync_t*) data;
6229 // Sometimes don't want to draw
6230 // normally because another update has taken precedent such as panning the display
6231 // which means this pixmap is no longer valid
6232 if ( ds->vtl->draw_sync_do ) {
6233 gdk_threads_enter();
6234 gdk_draw_drawable (ds->drawable,
6237 0, 0, 0, 0, -1, -1);
6238 ds->vtl->draw_sync_done = TRUE;
6239 gdk_threads_leave();
6244 static gchar* distance_string (gdouble distance)
6248 /* draw label with distance */
6249 vik_units_distance_t dist_units = a_vik_get_units_distance ();
6250 switch (dist_units) {
6251 case VIK_UNITS_DISTANCE_MILES:
6252 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
6253 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
6254 } else if (distance < 1609.4) {
6255 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
6257 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
6261 // VIK_UNITS_DISTANCE_KILOMETRES
6262 if (distance >= 1000 && distance < 100000) {
6263 g_sprintf(str, "%3.2f km", distance/1000.0);
6264 } else if (distance < 1000) {
6265 g_sprintf(str, "%d m", (int)distance);
6267 g_sprintf(str, "%d km", (int)distance/1000);
6271 return g_strdup (str);
6275 * Actually set the message in statusbar
6277 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
6279 // Only show elevation data when track has some elevation properties
6280 gchar str_gain_loss[64];
6281 str_gain_loss[0] = '\0';
6283 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
6284 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
6285 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
6287 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
6290 // Write with full gain/loss information
6291 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
6292 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
6297 * Figure out what information should be set in the statusbar and then write it
6299 static void update_statusbar ( VikTrwLayer *vtl )
6301 // Get elevation data
6302 gdouble elev_gain, elev_loss;
6303 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6305 /* Find out actual distance of current track */
6306 gdouble distance = vik_track_get_length (vtl->current_track);
6307 gchar *str = distance_string (distance);
6309 statusbar_write (str, elev_gain, elev_loss, vtl);
6315 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
6317 /* if we haven't sync'ed yet, we don't have time to do more. */
6318 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
6319 GList *iter = g_list_last ( vtl->current_track->trackpoints );
6320 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
6322 static GdkPixmap *pixmap = NULL;
6324 // Need to check in case window has been resized
6325 w1 = vik_viewport_get_width(vvp);
6326 h1 = vik_viewport_get_height(vvp);
6328 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6330 gdk_drawable_get_size (pixmap, &w2, &h2);
6331 if (w1 != w2 || h1 != h2) {
6332 g_object_unref ( G_OBJECT ( pixmap ) );
6333 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6336 // Reset to background
6337 gdk_draw_drawable (pixmap,
6338 vtl->current_track_newpoint_gc,
6339 vik_viewport_get_pixmap(vvp),
6340 0, 0, 0, 0, -1, -1);
6342 draw_sync_t *passalong;
6345 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
6347 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
6348 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
6349 // thus when we come to reset to the background it would include what we have already drawn!!
6350 gdk_draw_line ( pixmap,
6351 vtl->current_track_newpoint_gc,
6352 x1, y1, event->x, event->y );
6353 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
6355 /* Find out actual distance of current track */
6356 gdouble distance = vik_track_get_length (vtl->current_track);
6358 // Now add distance to where the pointer is //
6361 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
6362 vik_coord_to_latlon ( &coord, &ll );
6363 distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
6365 // Get elevation data
6366 gdouble elev_gain, elev_loss;
6367 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6369 // Adjust elevation data (if available) for the current pointer position
6371 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
6372 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
6373 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
6374 // Adjust elevation of last track point
6375 if ( elev_new > last_tpt->altitude )
6377 elev_gain += elev_new - last_tpt->altitude;
6380 elev_loss += last_tpt->altitude - elev_new;
6384 gchar *str = distance_string (distance);
6386 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
6387 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
6389 pango_layout_set_text (pl, str, -1);
6391 pango_layout_get_pixel_size ( pl, &wd, &hd );
6394 // offset from cursor a bit depending on font size
6398 // Create a background block to make the text easier to read over the background map
6399 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
6400 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
6401 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
6403 g_object_unref ( G_OBJECT ( pl ) );
6404 g_object_unref ( G_OBJECT ( background_block_gc ) );
6406 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
6407 passalong->vtl = vtl;
6408 passalong->pixmap = pixmap;
6409 passalong->drawable = GTK_WIDGET(vvp)->window;
6410 passalong->gc = vtl->current_track_newpoint_gc;
6412 // Update statusbar with full gain/loss information
6413 statusbar_write (str, elev_gain, elev_loss, vtl);
6417 // draw pixmap when we have time to
6418 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
6419 vtl->draw_sync_done = FALSE;
6420 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
6422 return VIK_LAYER_TOOL_ACK;
6425 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
6427 if ( vtl->current_track && event->keyval == GDK_Escape ) {
6428 vtl->current_track = NULL;
6429 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6431 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
6433 if ( vtl->current_track->trackpoints )
6435 GList *last = g_list_last(vtl->current_track->trackpoints);
6436 g_free ( last->data );
6437 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6440 update_statusbar ( vtl );
6442 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6448 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6452 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6455 if ( event->button == 2 ) {
6456 // As the display is panning, the new track pixmap is now invalid so don't draw it
6457 // otherwise this drawing done results in flickering back to an old image
6458 vtl->draw_sync_do = FALSE;
6462 if ( event->button == 3 && vtl->current_track )
6465 if ( vtl->current_track->trackpoints )
6467 GList *last = g_list_last(vtl->current_track->trackpoints);
6468 g_free ( last->data );
6469 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6471 update_statusbar ( vtl );
6473 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6477 if ( event->type == GDK_2BUTTON_PRESS )
6479 /* subtract last (duplicate from double click) tp then end */
6480 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
6482 GList *last = g_list_last(vtl->current_track->trackpoints);
6483 g_free ( last->data );
6484 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6485 /* undo last, then end */
6486 vtl->current_track = NULL;
6488 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6492 if ( ! vtl->current_track )
6494 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
6495 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
6497 vtl->current_track = vik_track_new();
6498 vtl->current_track->visible = TRUE;
6499 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
6501 /* incase it was created by begin track */
6502 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
6507 tp = vik_trackpoint_new();
6508 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
6510 /* snap to other TP */
6511 if ( event->state & GDK_CONTROL_MASK )
6513 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6515 tp->coord = other_tp->coord;
6518 tp->newsegment = FALSE;
6519 tp->has_timestamp = FALSE;
6521 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
6522 /* Auto attempt to get elevation from DEM data (if it's available) */
6523 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
6525 vtl->ct_x1 = vtl->ct_x2;
6526 vtl->ct_y1 = vtl->ct_y2;
6527 vtl->ct_x2 = event->x;
6528 vtl->ct_y2 = event->y;
6530 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6534 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6536 if ( event->button == 2 ) {
6537 // Pan moving ended - enable potential point drawing again
6538 vtl->draw_sync_do = TRUE;
6539 vtl->draw_sync_done = TRUE;
6543 /*** New waypoint ****/
6545 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6550 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6553 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6555 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6556 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
6557 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6562 /*** Edit trackpoint ****/
6564 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6566 tool_ed_t *t = g_new(tool_ed_t, 1);
6572 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6574 tool_ed_t *t = data;
6575 VikViewport *vvp = t->vvp;
6576 TPSearchParams params;
6577 /* OUTDATED DOCUMENTATION:
6578 find 5 pixel range on each side. then put these UTM, and a pointer
6579 to the winning track name (and maybe the winning track itself), and a
6580 pointer to the winning trackpoint, inside an array or struct. pass
6581 this along, do a foreach on the tracks which will do a foreach on the
6584 params.x = event->x;
6585 params.y = event->y;
6586 params.closest_track_id = NULL;
6587 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6588 params.closest_tp = NULL;
6590 if ( event->button != 1 )
6593 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6596 if ( !vtl->vl.visible || !vtl->tracks_visible )
6599 if ( vtl->current_tpl )
6601 /* 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.) */
6602 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6603 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6608 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6610 if ( current_tr->visible &&
6611 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
6612 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6613 marker_begin_move ( t, event->x, event->y );
6619 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6621 if ( params.closest_tp )
6623 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
6624 vtl->current_tpl = params.closest_tpl;
6625 vtl->current_tp_id = params.closest_track_id;
6626 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
6627 trw_layer_tpwin_init ( vtl );
6628 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
6629 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6633 /* these aren't the droids you're looking for */
6637 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6639 tool_ed_t *t = data;
6640 VikViewport *vvp = t->vvp;
6642 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6648 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6651 if ( event->state & GDK_CONTROL_MASK )
6653 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6654 if ( tp && tp != vtl->current_tpl->data )
6655 new_coord = tp->coord;
6657 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6660 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6661 marker_moveto ( t, x, y );
6669 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6671 tool_ed_t *t = data;
6672 VikViewport *vvp = t->vvp;
6674 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6676 if ( event->button != 1)
6681 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6684 if ( event->state & GDK_CONTROL_MASK )
6686 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6687 if ( tp && tp != vtl->current_tpl->data )
6688 new_coord = tp->coord;
6691 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6693 marker_end_move ( t );
6695 /* diff dist is diff from orig */
6697 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6699 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6706 #ifdef VIK_CONFIG_GOOGLE
6707 /*** Route Finder ***/
6708 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6713 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6716 if ( !vtl ) return FALSE;
6717 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6718 if ( event->button == 3 && vtl->route_finder_current_track ) {
6720 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6722 vtl->route_finder_coord = *new_end;
6724 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6725 /* remove last ' to:...' */
6726 if ( vtl->route_finder_current_track->comment ) {
6727 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6728 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6729 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6730 last_to - vtl->route_finder_current_track->comment - 1);
6731 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6736 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6737 struct LatLon start, end;
6738 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6739 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6742 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6743 vik_coord_to_latlon ( &(tmp), &end );
6744 vtl->route_finder_coord = tmp; /* for continuations */
6746 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6747 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6748 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6750 vtl->route_finder_check_added_track = TRUE;
6751 vtl->route_finder_started = FALSE;
6754 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6755 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6756 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6757 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6758 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6759 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
6762 /* see if anything was done -- a track was added or appended to */
6763 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6764 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 ) );
6765 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6766 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6767 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6768 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6770 vtl->route_finder_added_track = NULL;
6771 vtl->route_finder_check_added_track = FALSE;
6772 vtl->route_finder_append = FALSE;
6774 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6776 vtl->route_finder_started = TRUE;
6777 vtl->route_finder_coord = tmp;
6778 vtl->route_finder_current_track = NULL;
6784 /*** Show picture ****/
6786 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6791 /* Params are: vvp, event, last match found or NULL */
6792 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6794 if ( wp->image && wp->visible )
6796 gint x, y, slackx, slacky;
6797 GdkEventButton *event = (GdkEventButton *) params[1];
6799 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6800 slackx = wp->image_width / 2;
6801 slacky = wp->image_height / 2;
6802 if ( x <= event->x + slackx && x >= event->x - slackx
6803 && y <= event->y + slacky && y >= event->y - slacky )
6805 params[2] = wp->image; /* we've found a match. however continue searching
6806 * since we want to find the last match -- that
6807 * is, the match that was drawn last. */
6812 static void trw_layer_show_picture ( gpointer pass_along[6] )
6814 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6816 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
6819 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6820 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6821 g_free ( quoted_file );
6822 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6824 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() );
6825 g_error_free ( err );
6828 #endif /* WINDOWS */
6831 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6833 gpointer params[3] = { vvp, event, NULL };
6834 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6836 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6839 static gpointer pass_along[6];
6840 pass_along[0] = vtl;
6841 pass_along[5] = params[2];
6842 trw_layer_show_picture ( pass_along );
6843 return TRUE; /* found a match */
6846 return FALSE; /* go through other layers, searching for a match */
6849 /***************************************************************************
6851 ***************************************************************************/
6857 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6859 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6860 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6863 /* Structure for thumbnail creating data used in the background thread */
6865 VikTrwLayer *vtl; // Layer needed for redrawing
6866 GSList *pics; // Image list
6867 } thumbnail_create_thread_data;
6869 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6871 guint total = g_slist_length(tctd->pics), done = 0;
6872 while ( tctd->pics )
6874 a_thumbnails_create ( (gchar *) tctd->pics->data );
6875 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6877 return -1; /* Abort thread */
6879 tctd->pics = tctd->pics->next;
6882 // Redraw to show the thumbnails as they are now created
6883 if ( IS_VIK_LAYER(tctd->vtl) )
6884 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6889 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6891 while ( tctd->pics )
6893 g_free ( tctd->pics->data );
6894 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6899 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6901 if ( ! vtl->has_verified_thumbnails )
6903 GSList *pics = NULL;
6904 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6907 gint len = g_slist_length ( pics );
6908 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6909 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6912 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6914 (vik_thr_func) create_thumbnails_thread,
6916 (vik_thr_free_func) thumbnail_create_thread_free,
6924 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6926 return vtl->coord_mode;
6930 * Uniquify the whole layer
6931 * Also requires the layers panel as the names shown there need updating too
6932 * Returns whether the operation was successful or not
6934 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6937 vik_trw_layer_uniquify_tracks ( vtl, vlp );
6938 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
6944 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
6946 vik_coord_convert ( &(wp->coord), *dest_mode );
6949 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6951 vik_track_convert ( tr, *dest_mode );
6954 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6956 if ( vtl->coord_mode != dest_mode )
6958 vtl->coord_mode = dest_mode;
6959 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6960 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6964 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
6966 vtl->menu_selection = selection;
6969 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
6971 return (vtl->menu_selection);
6974 /* ----------- Downloading maps along tracks --------------- */
6976 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6978 /* TODO: calculating based on current size of viewport */
6979 const gdouble w_at_zoom_0_125 = 0.0013;
6980 const gdouble h_at_zoom_0_125 = 0.0011;
6981 gdouble zoom_factor = zoom_level/0.125;
6983 wh->lat = h_at_zoom_0_125 * zoom_factor;
6984 wh->lon = w_at_zoom_0_125 * zoom_factor;
6986 return 0; /* all OK */
6989 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6991 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6992 (dist->lat >= ABS(to->north_south - from->north_south)))
6995 VikCoord *coord = g_malloc(sizeof(VikCoord));
6996 coord->mode = VIK_COORD_LATLON;
6998 if (ABS(gradient) < 1) {
6999 if (from->east_west > to->east_west)
7000 coord->east_west = from->east_west - dist->lon;
7002 coord->east_west = from->east_west + dist->lon;
7003 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
7005 if (from->north_south > to->north_south)
7006 coord->north_south = from->north_south - dist->lat;
7008 coord->north_south = from->north_south + dist->lat;
7009 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
7015 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
7017 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
7018 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
7020 VikCoord *next = from;
7022 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
7024 list = g_list_prepend(list, next);
7030 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
7032 typedef struct _Rect {
7037 #define GLRECT(iter) ((Rect *)((iter)->data))
7040 GList *rects_to_download = NULL;
7043 if (get_download_area_width(vvp, zoom_level, &wh))
7046 GList *iter = tr->trackpoints;
7050 gboolean new_map = TRUE;
7051 VikCoord *cur_coord, tl, br;
7054 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
7056 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7057 rect = g_malloc(sizeof(Rect));
7060 rect->center = *cur_coord;
7061 rects_to_download = g_list_prepend(rects_to_download, rect);
7066 gboolean found = FALSE;
7067 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7068 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7079 GList *fillins = NULL;
7080 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
7081 /* seems that ATM the function get_next_coord works only for LATLON */
7082 if ( cur_coord->mode == VIK_COORD_LATLON ) {
7083 /* fill-ins for far apart points */
7084 GList *cur_rect, *next_rect;
7085 for (cur_rect = rects_to_download;
7086 (next_rect = cur_rect->next) != NULL;
7087 cur_rect = cur_rect->next) {
7088 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
7089 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
7090 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
7094 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
7097 GList *iter = fillins;
7099 cur_coord = (VikCoord *)(iter->data);
7100 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7101 rect = g_malloc(sizeof(Rect));
7104 rect->center = *cur_coord;
7105 rects_to_download = g_list_prepend(rects_to_download, rect);
7110 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7111 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7115 for (iter = fillins; iter; iter = iter->next)
7117 g_list_free(fillins);
7119 if (rects_to_download) {
7120 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
7121 g_free(rect_iter->data);
7122 g_list_free(rects_to_download);
7126 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7129 gint selected_map, default_map;
7130 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
7131 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
7132 gint selected_zoom, default_zoom;
7136 VikTrwLayer *vtl = pass_along[0];
7137 VikLayersPanel *vlp = pass_along[1];
7138 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7139 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
7141 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7142 int num_maps = g_list_length(vmls);
7145 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7149 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
7150 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
7152 gchar **np = map_names;
7153 VikMapsLayer **lp = map_layers;
7154 for (i = 0; i < num_maps; i++) {
7155 gboolean dup = FALSE;
7156 vml = (VikMapsLayer *)(vmls->data);
7157 for (j = 0; j < i; j++) { /* no duplicate allowed */
7158 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
7165 *np++ = vik_maps_layer_get_map_label(vml);
7171 num_maps = lp - map_layers;
7173 for (default_map = 0; default_map < num_maps; default_map++) {
7174 /* TODO: check for parent layer's visibility */
7175 if (VIK_LAYER(map_layers[default_map])->visible)
7178 default_map = (default_map == num_maps) ? 0 : default_map;
7180 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
7181 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
7182 if (cur_zoom == zoom_vals[default_zoom])
7185 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
7187 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
7190 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7193 for (i = 0; i < num_maps; i++)
7194 g_free(map_names[i]);
7202 /**** lowest waypoint number calculation ***/
7203 static gint highest_wp_number_name_to_number(const gchar *name) {
7204 if ( strlen(name) == 3 ) {
7206 if ( n < 100 && name[0] != '0' )
7208 if ( n < 10 && name[0] != '0' )
7216 static void highest_wp_number_reset(VikTrwLayer *vtl)
7218 vtl->highest_wp_number = -1;
7221 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
7223 /* if is bigger that top, add it */
7224 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
7225 if ( new_wp_num > vtl->highest_wp_number )
7226 vtl->highest_wp_number = new_wp_num;
7229 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
7231 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
7232 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
7233 if ( vtl->highest_wp_number == old_wp_num ) {
7235 vtl->highest_wp_number--;
7237 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7238 /* search down until we find something that *does* exist */
7240 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
7241 vtl->highest_wp_number--;
7242 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7247 /* get lowest unused number */
7248 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
7251 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
7253 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
7254 return g_strdup(buf);