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_DIRECTIONS
77 // This is currently broken as Google have disabled the KML output in Google Maps API v3
78 // It has been ifdefed out in the hope that Route Finding functionality will be restored one day...
79 // Only have 'JSON' and 'XML' see:
80 // https://developers.google.com/maps/documentation/directions/#DirectionsResponses
81 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
84 #define VIK_TRW_LAYER_TRACK_GC 16
85 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
86 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
87 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
88 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
89 #define VIK_TRW_LAYER_TRACK_GC_SLOW 13
90 #define VIK_TRW_LAYER_TRACK_GC_AVER 14
91 #define VIK_TRW_LAYER_TRACK_GC_FAST 15
93 #define DRAWMODE_BY_TRACK 0
94 #define DRAWMODE_BY_SPEED 1
95 #define DRAWMODE_ALL_BLACK 2
96 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
97 // as we are (re)calculating the colour for every point
102 /* this is how it knows when you click if you are clicking close to a trackpoint. */
103 #define TRACKPOINT_SIZE_APPROX 5
104 #define WAYPOINT_SIZE_APPROX 5
106 #define MIN_STOP_LENGTH 15
107 #define MAX_STOP_LENGTH 86400
108 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
109 /* this is multiplied by user-inputted value from 1-100. */
111 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
113 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
115 FS_XX_SMALL = 0, // 'xx-small'
118 FS_MEDIUM, // DEFAULT
125 struct _VikTrwLayer {
128 GHashTable *tracks_iters;
129 GHashTable *waypoints_iters;
130 GHashTable *waypoints;
131 GtkTreeIter waypoints_iter, tracks_iter;
132 gboolean tracks_visible, waypoints_visible;
135 guint8 drawpoints_size;
136 guint8 drawelevation;
137 guint8 elevation_factor;
141 guint8 drawdirections;
142 guint8 drawdirections_size;
143 guint8 line_thickness;
144 guint8 bg_line_thickness;
148 gboolean wp_draw_symbols;
149 font_size_t wp_font_size;
151 gdouble track_draw_speed_factor;
153 GdkGC *current_track_gc;
154 // Separate GC for a track's potential new point as drawn via separate method
155 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
156 GdkGC *current_track_newpoint_gc;
159 GdkGC *waypoint_text_gc;
160 GdkGC *waypoint_bg_gc;
161 VikTrack *current_track;
162 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
163 gboolean draw_sync_done;
164 gboolean draw_sync_do;
166 VikCoordMode coord_mode;
168 /* wp editing tool */
169 VikWaypoint *current_wp;
170 gpointer current_wp_id;
172 gboolean waypoint_rightclick;
174 /* track editing tool */
176 VikTrack *current_tp_track;
177 gpointer current_tp_id;
178 VikTrwLayerTpwin *tpwin;
180 /* track editing tool -- more specifically, moving tps */
183 /* route finder tool */
184 gboolean route_finder_started;
185 VikCoord route_finder_coord;
186 gboolean route_finder_check_added_track;
187 VikTrack *route_finder_added_track;
188 VikTrack *route_finder_current_track;
189 gboolean route_finder_append;
196 guint16 image_cache_size;
198 /* for waypoint text */
199 PangoLayout *wplabellayout;
201 gboolean has_verified_thumbnails;
203 GtkMenu *wp_right_click_menu;
204 GtkMenu *track_right_click_menu;
207 VikStdLayerMenuItem menu_selection;
209 gint highest_wp_number;
212 /* A caached waypoint image. */
215 gchar *image; /* filename */
218 struct DrawingParams {
222 guint16 width, height;
223 gdouble cc; // Cosine factor in track directions
224 gdouble ss; // Sine factor in track directions
225 const VikCoord *center;
227 gboolean one_zone, lat_lon;
228 gdouble ce1, ce2, cn1, cn2;
231 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
233 static void trw_layer_delete_item ( gpointer pass_along[6] );
234 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
235 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
237 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
238 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] );
239 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
241 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
242 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
244 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
245 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
247 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
248 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
249 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
250 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
251 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
252 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
253 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
254 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
255 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
256 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
257 static void trw_layer_append_track ( gpointer pass_along[6] );
258 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
259 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
260 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
261 static void trw_layer_split_segments ( gpointer pass_along[6] );
262 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
263 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
264 static void trw_layer_reverse ( gpointer pass_along[6] );
265 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
266 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
267 static void trw_layer_show_picture ( gpointer pass_along[6] );
268 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
270 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
271 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
272 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
273 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
274 static void trw_layer_new_wp ( gpointer lav[2] );
275 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
276 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
277 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
278 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
279 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
280 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
281 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
282 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
283 #ifdef VIK_CONFIG_GEOTAG
284 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
285 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
286 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
287 static void trw_layer_geotagging ( gpointer lav[2] );
289 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
290 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
291 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
293 #ifdef VIK_CONFIG_OPENSTREETMAP
294 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
296 #ifdef VIK_CONFIG_GEOCACHES
297 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
299 #ifdef VIK_CONFIG_GEOTAG
300 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
302 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
303 static void trw_layer_gps_upload ( gpointer lav[2] );
306 static void trw_layer_properties_item ( gpointer pass_along[7] );
307 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
308 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
310 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
311 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
312 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
314 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
315 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
316 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
317 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
319 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
320 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
321 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
322 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
323 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
324 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
325 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
326 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
327 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
328 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
329 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
330 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
331 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
332 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
333 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
334 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
335 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
336 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
337 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
338 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
339 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
340 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
343 static void cached_pixbuf_free ( CachedPixbuf *cp );
344 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
346 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
347 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
349 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
350 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
352 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
353 static void highest_wp_number_reset(VikTrwLayer *vtl);
354 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
355 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
357 // Note for the following tool GtkRadioActionEntry texts:
358 // the very first text value is an internal name not displayed anywhere
359 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
360 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
361 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
362 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
363 static VikToolInterface trw_layer_tools[] = {
364 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
365 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
366 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
368 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
370 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
371 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
372 (VikToolMouseFunc) tool_new_track_click,
373 (VikToolMouseMoveFunc) tool_new_track_move,
374 (VikToolMouseFunc) tool_new_track_release,
375 (VikToolKeyFunc) tool_new_track_key_press,
376 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
377 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
379 { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "<control><shift>B", N_("Begin Track"), 0 },
380 (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
381 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL,
383 GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
385 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
386 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
387 (VikToolMouseFunc) tool_edit_waypoint_click,
388 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
389 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
391 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
393 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
394 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
395 (VikToolMouseFunc) tool_edit_trackpoint_click,
396 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
397 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
399 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
401 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
402 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
403 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
405 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
407 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
408 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
409 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
410 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
412 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
415 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
417 /****** PARAMETERS ******/
419 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
420 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
422 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
423 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
425 #define MIN_POINT_SIZE 2
426 #define MAX_POINT_SIZE 10
428 #define MIN_ARROW_SIZE 3
429 #define MAX_ARROW_SIZE 20
431 static VikLayerParamScale params_scales[] = {
432 /* min max step digits */
433 { 1, 10, 1, 0 }, /* line_thickness */
434 { 0, 100, 1, 0 }, /* track draw speed factor */
435 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
436 /* 5 * step == how much to turn */
437 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
438 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
439 { 5, 500, 5, 0 }, // 5: image cache_size - " "
440 { 0, 8, 1, 0 }, // 6: Background line thickness
441 { 1, 64, 1, 0 }, /* wpsize */
442 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
443 { 1, 100, 1, 0 }, // 9: elevation factor
444 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
445 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
448 static gchar* params_font_sizes[] = {
449 N_("Extra Extra Small"),
455 N_("Extra Extra Large"),
458 VikLayerParam trw_layer_params[] = {
459 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
460 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
462 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
463 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
464 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
465 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
466 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
467 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
468 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
469 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
470 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
472 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
473 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
475 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
476 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
477 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
479 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
480 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
481 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
482 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
483 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
484 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
485 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
486 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
487 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
489 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
490 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
491 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
492 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
495 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
497 // Sublayer visibilities
534 *** 1) Add to trw_layer_params and enumeration
535 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
538 /****** END PARAMETERS ******/
540 static VikTrwLayer* trw_layer_new ( gint drawmode );
541 /* Layer Interface function definitions */
542 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
543 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
544 static void trw_layer_free ( VikTrwLayer *trwlayer );
545 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
546 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
547 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
548 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
549 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
550 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
551 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
552 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
553 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
554 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
555 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
556 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
557 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
558 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
559 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
560 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
561 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
562 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
563 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
564 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
565 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
566 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
567 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
568 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
569 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
570 /* End Layer Interface function definitions */
572 VikLayerInterface vik_trw_layer_interface = {
579 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
583 params_groups, /* params_groups */
584 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
588 (VikLayerFuncCreate) trw_layer_create,
589 (VikLayerFuncRealize) trw_layer_realize,
590 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
591 (VikLayerFuncFree) trw_layer_free,
593 (VikLayerFuncProperties) NULL,
594 (VikLayerFuncDraw) trw_layer_draw,
595 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
597 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
598 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
600 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
601 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
603 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
604 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
605 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
606 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
607 (VikLayerFuncLayerSelected) trw_layer_selected,
609 (VikLayerFuncMarshall) trw_layer_marshall,
610 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
612 (VikLayerFuncSetParam) trw_layer_set_param,
613 (VikLayerFuncGetParam) trw_layer_get_param,
615 (VikLayerFuncReadFileData) a_gpspoint_read_file,
616 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
618 (VikLayerFuncDeleteItem) trw_layer_del_item,
619 (VikLayerFuncCutItem) trw_layer_cut_item,
620 (VikLayerFuncCopyItem) trw_layer_copy_item,
621 (VikLayerFuncPasteItem) trw_layer_paste_item,
622 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
624 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
626 (VikLayerFuncSelectClick) trw_layer_select_click,
627 (VikLayerFuncSelectMove) trw_layer_select_move,
628 (VikLayerFuncSelectRelease) trw_layer_select_release,
629 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
638 GType vik_trw_layer_get_type ()
640 static GType vtl_type = 0;
644 static const GTypeInfo vtl_info =
646 sizeof (VikTrwLayerClass),
647 NULL, /* base_init */
648 NULL, /* base_finalize */
649 NULL, /* class init */
650 NULL, /* class_finalize */
651 NULL, /* class_data */
652 sizeof (VikTrwLayer),
654 NULL /* instance init */
656 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
662 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
664 static gpointer pass_along[6];
670 pass_along[1] = NULL;
671 pass_along[2] = GINT_TO_POINTER (subtype);
672 pass_along[3] = sublayer;
673 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
674 pass_along[5] = NULL;
676 trw_layer_delete_item ( pass_along );
679 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
681 static gpointer pass_along[6];
687 pass_along[1] = NULL;
688 pass_along[2] = GINT_TO_POINTER (subtype);
689 pass_along[3] = sublayer;
690 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
691 pass_along[5] = NULL;
693 trw_layer_copy_item_cb(pass_along);
694 trw_layer_cut_item_cb(pass_along);
697 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
699 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
700 gint subtype = GPOINTER_TO_INT (pass_along[2]);
701 gpointer * sublayer = pass_along[3];
705 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
709 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
710 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
711 if ( wp && wp->name )
714 name = NULL; // Broken :(
717 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
718 if ( trk && trk->name )
721 name = NULL; // Broken :(
724 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
725 subtype, len, name, data);
729 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
731 trw_layer_copy_item_cb(pass_along);
732 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
733 trw_layer_delete_item(pass_along);
736 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
747 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
749 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
750 // 'Simple' memory copy of byte array from the marshalling above
751 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
752 fi = g_malloc ( *len );
754 memcpy(fi->data, id, il);
757 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
758 // less magic than before...
759 *len = sizeof(FlatItem) + 1 + il;
760 fi = g_malloc ( *len );
762 memcpy(fi->data, id, il);
766 *item = (guint8 *)fi;
769 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
771 FlatItem *fi = (FlatItem *) item;
773 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
778 w = vik_waypoint_unmarshall(fi->data, fi->len);
779 // When copying - we'll create a new name based on the original
780 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
781 vik_trw_layer_add_waypoint ( vtl, name, w );
782 waypoint_convert (NULL, w, &vtl->coord_mode);
784 // Consider if redraw necessary for the new item
785 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
786 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
789 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
794 t = vik_track_unmarshall(fi->data, fi->len);
795 // When copying - we'll create a new name based on the original
796 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
797 vik_trw_layer_add_track ( vtl, name, t );
798 track_convert (name, t, &vtl->coord_mode);
800 // Consider if redraw necessary for the new item
801 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
802 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
808 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
815 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
819 case PARAM_TV: vtl->tracks_visible = data.b; break;
820 case PARAM_WV: vtl->waypoints_visible = data.b; break;
821 case PARAM_DM: vtl->drawmode = data.u; break;
822 case PARAM_DP: vtl->drawpoints = data.b; break;
824 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
825 vtl->drawpoints_size = data.u;
827 case PARAM_DE: vtl->drawelevation = data.b; break;
828 case PARAM_DS: vtl->drawstops = data.b; break;
829 case PARAM_DL: vtl->drawlines = data.b; break;
830 case PARAM_DD: vtl->drawdirections = data.b; break;
832 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
833 vtl->drawdirections_size = data.u;
835 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
836 vtl->stop_length = data.u;
838 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
839 vtl->elevation_factor = data.u;
841 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
843 vtl->line_thickness = data.u;
844 trw_layer_new_track_gcs ( vtl, vp );
847 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
849 vtl->bg_line_thickness = data.u;
850 trw_layer_new_track_gcs ( vtl, vp );
853 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
854 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
855 case PARAM_DLA: vtl->drawlabels = data.b; break;
856 case PARAM_DI: vtl->drawimages = data.b; break;
857 case PARAM_IS: if ( data.u != vtl->image_size )
859 vtl->image_size = data.u;
860 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
861 g_queue_free ( vtl->image_cache );
862 vtl->image_cache = g_queue_new ();
865 case PARAM_IA: vtl->image_alpha = data.u; break;
866 case PARAM_ICS: vtl->image_cache_size = data.u;
867 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
868 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
870 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
871 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
872 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
873 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
874 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
875 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
876 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
877 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
882 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
884 VikLayerParamData rv;
887 case PARAM_TV: rv.b = vtl->tracks_visible; break;
888 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
889 case PARAM_DM: rv.u = vtl->drawmode; break;
890 case PARAM_DP: rv.b = vtl->drawpoints; break;
891 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
892 case PARAM_DE: rv.b = vtl->drawelevation; break;
893 case PARAM_EF: rv.u = vtl->elevation_factor; break;
894 case PARAM_DS: rv.b = vtl->drawstops; break;
895 case PARAM_SL: rv.u = vtl->stop_length; break;
896 case PARAM_DL: rv.b = vtl->drawlines; break;
897 case PARAM_DD: rv.b = vtl->drawdirections; break;
898 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
899 case PARAM_LT: rv.u = vtl->line_thickness; break;
900 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
901 case PARAM_DLA: rv.b = vtl->drawlabels; break;
902 case PARAM_DI: rv.b = vtl->drawimages; break;
903 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
904 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
905 case PARAM_IS: rv.u = vtl->image_size; break;
906 case PARAM_IA: rv.u = vtl->image_alpha; break;
907 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
908 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
909 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
910 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
911 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
912 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
913 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
914 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
915 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
920 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
931 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
932 a_gpx_write_file(vtl, f, NULL);
933 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
936 g_file_get_contents(tmpname, &dd, &dl, NULL);
937 *len = sizeof(pl) + pl + dl;
938 *data = g_malloc(*len);
939 memcpy(*data, &pl, sizeof(pl));
940 memcpy(*data + sizeof(pl), pd, pl);
941 memcpy(*data + sizeof(pl) + pl, dd, dl);
950 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
952 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
958 memcpy(&pl, data, sizeof(pl));
960 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
963 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
964 g_critical("couldn't open temp file");
967 fwrite(data, len - pl - sizeof(pl), 1, f);
969 a_gpx_read_file(rv, f);
977 // Keep interesting hash function at least visible
979 static guint strcase_hash(gconstpointer v)
981 // 31 bit hash function
984 gchar s[128]; // malloc is too slow for reading big files
987 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
988 p[i] = toupper(t[i]);
994 for (p += 1; *p != '\0'; p++)
995 h = (h << 5) - h + *p;
1002 static VikTrwLayer* trw_layer_new ( gint drawmode )
1004 if (trw_layer_params[PARAM_DM].widget_data == NULL)
1005 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
1006 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
1007 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
1009 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1010 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1012 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1013 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1015 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1016 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1017 // and with normal PC processing capabilities - it has negligibile performance impact
1018 // This also minimized the amount of rework - as the management of the hash tables already exists.
1020 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1021 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1022 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1024 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1025 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1026 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1027 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1029 /* TODO: constants at top */
1030 rv->waypoints_visible = rv->tracks_visible = TRUE;
1031 rv->drawmode = drawmode;
1032 rv->drawpoints = TRUE;
1033 rv->drawpoints_size = MIN_POINT_SIZE;
1034 rv->drawdirections_size = 5;
1035 rv->drawstops = FALSE;
1036 rv->drawelevation = FALSE;
1037 rv->elevation_factor = 30;
1038 rv->stop_length = 60;
1039 rv->drawlines = TRUE;
1040 rv->wplabellayout = NULL;
1041 rv->wp_right_click_menu = NULL;
1042 rv->track_right_click_menu = NULL;
1043 rv->waypoint_gc = NULL;
1044 rv->waypoint_text_gc = NULL;
1045 rv->waypoint_bg_gc = NULL;
1046 rv->track_gc = NULL;
1047 rv->track_draw_speed_factor = 30.0;
1048 rv->line_thickness = 1;
1049 rv->bg_line_thickness = 0;
1050 rv->current_wp = NULL;
1051 rv->current_wp_id = NULL;
1052 rv->current_track = NULL;
1053 rv->current_tpl = NULL;
1054 rv->current_tp_track = NULL;
1055 rv->current_tp_id = NULL;
1056 rv->moving_tp = FALSE;
1057 rv->moving_wp = FALSE;
1059 rv->draw_sync_done = TRUE;
1060 rv->draw_sync_do = TRUE;
1062 rv->route_finder_started = FALSE;
1063 rv->route_finder_check_added_track = FALSE;
1064 rv->route_finder_current_track = NULL;
1065 rv->route_finder_append = FALSE;
1067 rv->waypoint_rightclick = FALSE;
1069 rv->image_cache = g_queue_new();
1070 rv->image_size = 64;
1071 rv->image_alpha = 255;
1072 rv->image_cache_size = 300;
1073 rv->drawimages = TRUE;
1074 rv->drawlabels = TRUE;
1079 static void trw_layer_free ( VikTrwLayer *trwlayer )
1081 g_hash_table_destroy(trwlayer->waypoints);
1082 g_hash_table_destroy(trwlayer->tracks);
1084 /* ODC: replace with GArray */
1085 trw_layer_free_track_gcs ( trwlayer );
1087 if ( trwlayer->wp_right_click_menu )
1088 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1090 if ( trwlayer->track_right_click_menu )
1091 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1093 if ( trwlayer->wplabellayout != NULL)
1094 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1096 if ( trwlayer->waypoint_gc != NULL )
1097 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1099 if ( trwlayer->waypoint_text_gc != NULL )
1100 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1102 if ( trwlayer->waypoint_bg_gc != NULL )
1103 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1105 if ( trwlayer->tpwin != NULL )
1106 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1108 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1109 g_queue_free ( trwlayer->image_cache );
1112 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1116 dp->xmpp = vik_viewport_get_xmpp ( vp );
1117 dp->ympp = vik_viewport_get_ympp ( vp );
1118 dp->width = vik_viewport_get_width ( vp );
1119 dp->height = vik_viewport_get_height ( vp );
1120 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1121 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1123 dp->center = vik_viewport_get_center ( vp );
1124 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1125 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1130 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1131 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1132 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1134 dp->ce1 = dp->center->east_west-w2;
1135 dp->ce2 = dp->center->east_west+w2;
1136 dp->cn1 = dp->center->north_south-h2;
1137 dp->cn2 = dp->center->north_south+h2;
1138 } else if ( dp->lat_lon ) {
1139 VikCoord upperleft, bottomright;
1140 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1141 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1142 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1143 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1144 dp->ce1 = upperleft.east_west;
1145 dp->ce2 = bottomright.east_west;
1146 dp->cn1 = bottomright.north_south;
1147 dp->cn2 = upperleft.north_south;
1150 dp->track_gc_iter = 0;
1154 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1155 * Here a simple traffic like light colour system is used:
1156 * . slow points are red
1157 * . average is yellow
1158 * . fast points are green
1160 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1163 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1164 if ( average_speed > 0 ) {
1165 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1166 if ( rv < low_speed )
1167 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1168 else if ( rv > high_speed )
1169 return VIK_TRW_LAYER_TRACK_GC_FAST;
1171 return VIK_TRW_LAYER_TRACK_GC_AVER;
1174 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1177 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1179 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1180 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1181 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1182 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1185 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1187 /* TODO: this function is a mess, get rid of any redundancy */
1188 GList *list = track->trackpoints;
1190 gboolean useoldvals = TRUE;
1192 gboolean drawpoints;
1194 gboolean drawelevation;
1195 gdouble min_alt, max_alt, alt_diff = 0;
1197 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1198 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1201 if ( dp->vtl->drawelevation )
1203 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1204 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1205 alt_diff = max_alt - min_alt;
1208 if ( ! track->visible )
1211 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1212 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1213 trw_layer_draw_track ( name, track, dp, TRUE );
1215 if ( drawing_white_background )
1216 drawpoints = drawstops = FALSE;
1218 drawpoints = dp->vtl->drawpoints;
1219 drawstops = dp->vtl->drawstops;
1222 gboolean drawing_highlight = FALSE;
1223 /* Current track - used for creation */
1224 if ( track == dp->vtl->current_track )
1225 main_gc = dp->vtl->current_track_gc;
1227 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1228 /* Draw all tracks of the layer in special colour */
1229 /* if track is member of selected layer or is the current selected track
1230 then draw in the highlight colour.
1231 NB this supercedes the drawmode */
1232 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1233 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1234 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1235 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1236 drawing_highlight = TRUE;
1239 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1240 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1242 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1246 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1247 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1249 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1254 int x, y, oldx, oldy;
1255 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1257 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1259 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1261 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1263 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1264 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1270 gdouble average_speed = 0.0;
1271 gdouble low_speed = 0.0;
1272 gdouble high_speed = 0.0;
1273 // If necessary calculate these values - which is done only once per track redraw
1274 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1275 // the percentage factor away from the average speed determines transistions between the levels
1276 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1277 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1278 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1281 while ((list = g_list_next(list)))
1283 tp = VIK_TRACKPOINT(list->data);
1284 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1286 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1287 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1288 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1289 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1290 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1292 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1295 * If points are the same in display coordinates, don't draw.
1297 if ( useoldvals && x == oldx && y == oldy )
1299 // Still need to process points to ensure 'stops' are drawn if required
1300 if ( drawstops && drawpoints && ! drawing_white_background && list->next &&
1301 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1302 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 );
1307 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1308 if ( drawpoints || dp->vtl->drawlines ) {
1309 // setup main_gc for both point and line drawing
1310 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1311 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1312 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1316 if ( drawpoints && ! drawing_white_background )
1321 * The concept of drawing stops is that a trackpoint
1322 * that is if the next trackpoint has a timestamp far into
1323 * the future, we draw a circle of 6x trackpoint size,
1324 * instead of a rectangle of 2x trackpoint size.
1325 * This is drawn first so the trackpoint will be drawn on top
1328 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1329 /* Stop point. Draw 6x circle. */
1330 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 );
1332 /* Regular point - draw 2x square. */
1333 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1336 /* Final point - draw 4x circle. */
1337 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 );
1340 if ((!tp->newsegment) && (dp->vtl->drawlines))
1343 /* UTM only: zone check */
1344 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1345 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1348 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1350 if ( drawing_white_background ) {
1351 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1355 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1357 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1359 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1364 tmp[1].y = oldy-FIXALTITUDE(list->data);
1366 tmp[2].y = y-FIXALTITUDE(list->next->data);
1371 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1372 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1374 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1375 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1377 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1382 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1383 // Draw an arrow at the mid point to show the direction of the track
1384 // Code is a rework from vikwindow::draw_ruler()
1385 gint midx = (oldx + x) / 2;
1386 gint midy = (oldy + y) / 2;
1388 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1389 // Avoid divide by zero and ensure at least 1 pixel big
1391 gdouble dx = (oldx - midx) / len;
1392 gdouble dy = (oldy - midy) / len;
1393 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1394 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1404 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1406 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1407 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1409 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1411 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1412 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1413 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1417 * If points are the same in display coordinates, don't draw.
1419 if ( x != oldx || y != oldy )
1421 if ( drawing_white_background )
1422 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1424 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1430 * If points are the same in display coordinates, don't draw.
1432 if ( x != oldx && y != oldy )
1434 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1435 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1443 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1444 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
1445 dp->track_gc_iter = 0;
1448 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1449 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1451 trw_layer_draw_track ( name, track, dp, FALSE );
1454 static void cached_pixbuf_free ( CachedPixbuf *cp )
1456 g_object_unref ( G_OBJECT(cp->pixbuf) );
1457 g_free ( cp->image );
1460 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1462 return strcmp ( cp->image, name );
1465 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1468 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1469 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1470 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1473 GdkPixbuf *sym = NULL;
1474 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1476 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1478 if ( wp->image && dp->vtl->drawimages )
1480 GdkPixbuf *pixbuf = NULL;
1483 if ( dp->vtl->image_alpha == 0)
1486 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1488 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1491 gchar *image = wp->image;
1492 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1493 if ( ! regularthumb )
1495 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1496 image = "\x12\x00"; /* this shouldn't occur naturally. */
1500 CachedPixbuf *cp = NULL;
1501 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1502 if ( dp->vtl->image_size == 128 )
1503 cp->pixbuf = regularthumb;
1506 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1507 g_assert ( cp->pixbuf );
1508 g_object_unref ( G_OBJECT(regularthumb) );
1510 cp->image = g_strdup ( image );
1512 /* needed so 'click picture' tool knows how big the pic is; we don't
1513 * store it in cp because they may have been freed already. */
1514 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1515 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1517 g_queue_push_head ( dp->vtl->image_cache, cp );
1518 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1519 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1521 pixbuf = cp->pixbuf;
1525 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1531 w = gdk_pixbuf_get_width ( pixbuf );
1532 h = gdk_pixbuf_get_height ( pixbuf );
1534 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1536 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1537 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1538 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1539 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1540 // Highlighted - so draw a little border around the chosen one
1541 // single line seems a little weak so draw 2 of them
1542 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1543 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1544 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1545 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1548 if ( dp->vtl->image_alpha == 255 )
1549 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1551 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1553 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1557 /* DRAW ACTUAL DOT */
1558 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1559 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 );
1561 else if ( wp == dp->vtl->current_wp ) {
1562 switch ( dp->vtl->wp_symbol ) {
1563 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;
1564 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;
1565 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;
1566 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 );
1567 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 );
1571 switch ( dp->vtl->wp_symbol ) {
1572 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;
1573 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;
1574 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;
1575 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 );
1576 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;
1580 if ( dp->vtl->drawlabels )
1582 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1583 gint label_x, label_y;
1585 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1587 // Could this stored in the waypoint rather than recreating each pass?
1588 gchar *fsize = NULL;
1589 switch (dp->vtl->wp_font_size) {
1590 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1591 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1592 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1593 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1594 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1595 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1596 default: fsize = g_strdup ( "medium" ); break;
1599 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1601 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1602 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1604 // Fallback if parse failure
1605 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1607 g_free ( wp_label_markup );
1610 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1611 label_x = x - width/2;
1613 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1615 label_y = y - dp->vtl->wp_size - height - 2;
1617 /* if highlight mode on, then draw background text in highlight colour */
1618 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1619 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1620 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1621 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1622 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1624 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1627 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1629 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1634 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1636 static struct DrawingParams dp;
1637 g_assert ( l != NULL );
1639 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1641 if ( l->tracks_visible )
1642 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1644 if (l->waypoints_visible)
1645 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1648 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1651 if ( vtl->track_bg_gc )
1653 g_object_unref ( vtl->track_bg_gc );
1654 vtl->track_bg_gc = NULL;
1656 if ( vtl->current_track_gc )
1658 g_object_unref ( vtl->current_track_gc );
1659 vtl->current_track_gc = NULL;
1661 if ( vtl->current_track_newpoint_gc )
1663 g_object_unref ( vtl->current_track_newpoint_gc );
1664 vtl->current_track_newpoint_gc = NULL;
1667 if ( ! vtl->track_gc )
1669 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1670 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1671 g_array_free ( vtl->track_gc, TRUE );
1672 vtl->track_gc = NULL;
1675 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1677 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1678 gint width = vtl->line_thickness;
1680 if ( vtl->track_gc )
1681 trw_layer_free_track_gcs ( vtl );
1683 if ( vtl->track_bg_gc )
1684 g_object_unref ( vtl->track_bg_gc );
1685 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1687 // Ensure new track drawing heeds line thickness setting
1688 // however always have a minium of 2, as 1 pixel is really narrow
1689 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1691 if ( vtl->current_track_gc )
1692 g_object_unref ( vtl->current_track_gc );
1693 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1694 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1696 // 'newpoint' gc is exactly the same as the current track gc
1697 if ( vtl->current_track_newpoint_gc )
1698 g_object_unref ( vtl->current_track_newpoint_gc );
1699 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1700 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1702 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1704 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1706 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1707 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1708 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1709 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1710 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1711 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1712 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1713 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1714 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1715 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1717 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1719 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1721 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1722 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1723 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1725 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1728 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1730 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1731 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1733 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1734 /* early exit, as the rest is GUI related */
1738 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1739 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1741 trw_layer_new_track_gcs ( rv, vp );
1743 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1744 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1745 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1746 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1748 rv->has_verified_thumbnails = FALSE;
1749 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1751 rv->wp_draw_symbols = TRUE;
1752 rv->wp_font_size = FS_MEDIUM;
1754 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1756 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1761 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1763 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1765 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1766 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 );
1768 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 );
1771 *new_iter = *((GtkTreeIter *) pass_along[1]);
1772 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1774 if ( ! track->visible )
1775 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1778 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1780 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1781 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1782 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 );
1784 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 );
1787 *new_iter = *((GtkTreeIter *) pass_along[1]);
1788 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1790 if ( ! wp->visible )
1791 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1795 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1798 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1800 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1801 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1803 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1805 if ( ! vtl->tracks_visible )
1806 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1808 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1810 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1811 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1813 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1816 if ( ! vtl->waypoints_visible )
1817 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1819 pass_along[0] = &(vtl->waypoints_iter);
1820 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1822 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1826 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1830 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1831 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1832 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1834 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1836 return (t->visible ^= 1);
1840 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1842 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1844 return (t->visible ^= 1);
1853 * Return a property about tracks for this layer
1855 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1857 return vtl->line_thickness;
1860 // Structure to hold multiple track information for a layer
1869 * Build up layer multiple track information via updating the tooltip_tracks structure
1871 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1873 tt->length = tt->length + vik_track_get_length (tr);
1875 // Ensure times are available
1876 if ( tr->trackpoints &&
1877 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1878 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1881 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1882 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1884 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1885 // Hence initialize to the first 'proper' value
1886 if ( tt->start_time == 0 )
1887 tt->start_time = t1;
1888 if ( tt->end_time == 0 )
1891 // Update find the earliest / last times
1892 if ( t1 < tt->start_time )
1893 tt->start_time = t1;
1894 if ( t2 > tt->end_time )
1897 // Keep track of total time
1898 // there maybe gaps within a track (eg segments)
1899 // but this should be generally good enough for a simple indicator
1900 tt->duration = tt->duration + (int)(t2-t1);
1905 * Generate tooltip text for the layer.
1906 * This is relatively complicated as it considers information for
1907 * no tracks, a single track or multiple tracks
1908 * (which may or may not have timing information)
1910 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1921 static gchar tmp_buf[128];
1924 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1926 // Safety check - I think these should always be valid
1927 if ( vtl->tracks && vtl->waypoints ) {
1928 tooltip_tracks tt = { 0.0, 0, 0 };
1929 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1931 GDate* gdate_start = g_date_new ();
1932 g_date_set_time_t (gdate_start, tt.start_time);
1934 GDate* gdate_end = g_date_new ();
1935 g_date_set_time_t (gdate_end, tt.end_time);
1937 if ( g_date_compare (gdate_start, gdate_end) ) {
1938 // Dates differ so print range on separate line
1939 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1940 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1941 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1944 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1945 if ( tt.start_time != 0 )
1946 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1950 if ( tt.length > 0.0 ) {
1951 gdouble len_in_units;
1953 // Setup info dependent on distance units
1954 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1955 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1956 len_in_units = VIK_METERS_TO_MILES(tt.length);
1959 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1960 len_in_units = tt.length/1000.0;
1963 // Timing information if available
1965 if ( tt.duration > 0 ) {
1966 g_snprintf (tbuf1, sizeof(tbuf1),
1967 _(" in %d:%02d hrs:mins"),
1968 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1970 g_snprintf (tbuf2, sizeof(tbuf2),
1971 _("\n%sTotal Length %.1f %s%s"),
1972 tbuf3, len_in_units, tbuf4, tbuf1);
1975 // Put together all the elements to form compact tooltip text
1976 g_snprintf (tmp_buf, sizeof(tmp_buf),
1977 _("Tracks: %d - Waypoints: %d%s"),
1978 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1980 g_date_free (gdate_start);
1981 g_date_free (gdate_end);
1988 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1992 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1993 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1994 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1996 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1998 // Could be a better way of handling strings - but this works...
1999 gchar time_buf1[20];
2000 gchar time_buf2[20];
2001 time_buf1[0] = '\0';
2002 time_buf2[0] = '\0';
2003 static gchar tmp_buf[100];
2004 // Compact info: Short date eg (11/20/99), duration and length
2005 // Hopefully these are the things that are most useful and so promoted into the tooltip
2006 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2007 // %x The preferred date representation for the current locale without the time.
2008 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2009 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2010 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2012 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2015 // Get length and consider the appropriate distance units
2016 gdouble tr_len = vik_track_get_length(tr);
2017 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2018 switch (dist_units) {
2019 case VIK_UNITS_DISTANCE_KILOMETRES:
2020 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2022 case VIK_UNITS_DISTANCE_MILES:
2023 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2032 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2034 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2035 // NB It's OK to return NULL
2046 * Function to show basic track point information on the statusbar
2048 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2051 switch (a_vik_get_units_height ()) {
2052 case VIK_UNITS_HEIGHT_FEET:
2053 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2056 //VIK_UNITS_HEIGHT_METRES:
2057 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2062 if ( trkpt->has_timestamp ) {
2063 // Compact date time format
2064 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2068 // Position is put later on, as this bit may not be seen if the display is not big enough,
2069 // one can easily use the current pointer position to see this if needed
2070 gchar *lat = NULL, *lon = NULL;
2071 static struct LatLon ll;
2072 vik_coord_to_latlon (&(trkpt->coord), &ll);
2073 a_coords_latlon_to_string ( &ll, &lat, &lon );
2076 // Again is put later on, as this bit may not be seen if the display is not big enough
2077 // trackname can be seen from the treeview (when enabled)
2078 // Also name could be very long to not leave room for anything else
2081 if ( vtl->current_tp_track ) {
2082 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2085 // Combine parts to make overall message
2086 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2087 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2094 * Function to show basic waypoint information on the statusbar
2096 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2099 switch (a_vik_get_units_height ()) {
2100 case VIK_UNITS_HEIGHT_FEET:
2101 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2104 //VIK_UNITS_HEIGHT_METRES:
2105 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2109 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2110 // one can easily use the current pointer position to see this if needed
2111 gchar *lat = NULL, *lon = NULL;
2112 static struct LatLon ll;
2113 vik_coord_to_latlon (&(wpt->coord), &ll);
2114 a_coords_latlon_to_string ( &ll, &lat, &lon );
2116 // Combine parts to make overall message
2119 // Add comment if available
2120 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2122 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2123 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2130 * General layer selection function, find out which bit is selected and take appropriate action
2132 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2135 l->current_wp = NULL;
2136 l->current_wp_id = NULL;
2137 trw_layer_cancel_current_tp ( l, FALSE );
2140 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2144 case VIK_TREEVIEW_TYPE_LAYER:
2146 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2147 /* Mark for redraw */
2152 case VIK_TREEVIEW_TYPE_SUBLAYER:
2156 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2158 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2159 /* Mark for redraw */
2163 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2165 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2166 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2167 /* Mark for redraw */
2171 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2173 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2174 /* Mark for redraw */
2178 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2180 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2182 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2183 // Show some waypoint info
2184 set_statusbar_msg_info_wpt ( l, wpt );
2185 /* Mark for redraw */
2192 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2201 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2206 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2211 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2213 return l->waypoints;
2217 * ATM use a case sensitive find
2218 * Finds the first one
2220 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2222 if ( wp && wp->name )
2223 if ( ! strcmp ( wp->name, name ) )
2229 * Get waypoint by name - not guaranteed to be unique
2230 * Finds the first one
2232 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2234 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2238 * ATM use a case sensitive find
2239 * Finds the first one
2241 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2243 if ( trk && trk->name )
2244 if ( ! strcmp ( trk->name, name ) )
2250 * Get track by name - not guaranteed to be unique
2251 * Finds the first one
2253 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2255 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2258 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2260 static VikCoord fixme;
2261 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2262 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2263 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2264 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2265 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2266 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2267 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2268 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2269 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2272 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2274 GList *tr = trk->trackpoints;
2275 static VikCoord fixme;
2279 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2280 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2281 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2282 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2283 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2284 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2285 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2286 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2287 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2292 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2294 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2295 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2297 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2298 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2299 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2300 maxmin[0].lat = wpt_maxmin[0].lat;
2303 maxmin[0].lat = trk_maxmin[0].lat;
2305 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2306 maxmin[0].lon = wpt_maxmin[0].lon;
2309 maxmin[0].lon = trk_maxmin[0].lon;
2311 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2312 maxmin[1].lat = wpt_maxmin[1].lat;
2315 maxmin[1].lat = trk_maxmin[1].lat;
2317 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2318 maxmin[1].lon = wpt_maxmin[1].lon;
2321 maxmin[1].lon = trk_maxmin[1].lon;
2325 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2327 /* 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... */
2328 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2329 trw_layer_find_maxmin (vtl, maxmin);
2330 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2334 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2335 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2340 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2343 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2344 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2346 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2349 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2351 /* First set the center [in case previously viewing from elsewhere] */
2352 /* Then loop through zoom levels until provided positions are in view */
2353 /* This method is not particularly fast - but should work well enough */
2354 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2356 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2357 vik_viewport_set_center_coord ( vvp, &coord );
2359 /* Convert into definite 'smallest' and 'largest' positions */
2360 struct LatLon minmin;
2361 if ( maxmin[0].lat < maxmin[1].lat )
2362 minmin.lat = maxmin[0].lat;
2364 minmin.lat = maxmin[1].lat;
2366 struct LatLon maxmax;
2367 if ( maxmin[0].lon > maxmin[1].lon )
2368 maxmax.lon = maxmin[0].lon;
2370 maxmax.lon = maxmin[1].lon;
2372 /* Never zoom in too far - generally not that useful, as too close ! */
2373 /* Always recalculate the 'best' zoom level */
2375 vik_viewport_set_zoom ( vvp, zoom );
2377 gdouble min_lat, max_lat, min_lon, max_lon;
2378 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2379 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2380 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2381 /* NB I think the logic used in this test to determine if the bounds is within view
2382 fails if track goes across 180 degrees longitude.
2383 Hopefully that situation is not too common...
2384 Mind you viking doesn't really do edge locations to well anyway */
2385 if ( min_lat < minmin.lat &&
2386 max_lat > minmin.lat &&
2387 min_lon < maxmax.lon &&
2388 max_lon > maxmax.lon )
2389 /* Found within zoom level */
2394 vik_viewport_set_zoom ( vvp, zoom );
2398 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2400 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2401 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2402 trw_layer_find_maxmin (vtl, maxmin);
2403 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2406 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2411 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2413 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])) ) ) {
2414 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2417 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2420 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2422 GtkWidget *file_selector;
2424 gboolean failed = FALSE;
2425 file_selector = gtk_file_chooser_dialog_new (title,
2427 GTK_FILE_CHOOSER_ACTION_SAVE,
2428 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2429 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2431 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2433 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2435 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2436 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2438 gtk_widget_hide ( file_selector );
2439 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2444 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2446 gtk_widget_hide ( file_selector );
2447 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2452 gtk_widget_destroy ( file_selector );
2454 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2457 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2459 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2462 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2464 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2467 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2469 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2470 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2471 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2472 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2474 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2476 g_free ( auto_save_name );
2479 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2481 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2482 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2483 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2484 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2486 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2488 g_free ( auto_save_name );
2492 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2495 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2497 gchar *name_used = NULL;
2500 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2501 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2503 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2507 gchar *quoted_file = g_shell_quote ( name_used );
2508 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2509 g_free ( quoted_file );
2510 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2512 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2513 g_error_free ( err );
2517 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2518 //g_remove ( name_used );
2519 // Perhaps should be deleted when the program ends?
2520 // For now leave it to the user to delete it / use system temp cleanup methods.
2521 g_free ( name_used );
2525 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2527 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2530 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2532 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2535 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2537 gpointer layer_and_vlp[2];
2538 layer_and_vlp[0] = pass_along[0];
2539 layer_and_vlp[1] = pass_along[1];
2540 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2542 if ( !trk || !trk->name )
2545 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2546 gchar *auto_save_name = g_strdup ( trk->name );
2547 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2548 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2550 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2552 g_free ( auto_save_name );
2556 VikWaypoint *wp; // input
2557 gpointer uuid; // output
2560 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2562 wpu_udata *user_data = udata;
2563 if ( wp == user_data->wp ) {
2564 user_data->uuid = id;
2570 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2572 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2573 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2574 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2576 GTK_RESPONSE_REJECT,
2578 GTK_RESPONSE_ACCEPT,
2581 GtkWidget *label, *entry;
2582 label = gtk_label_new(_("Waypoint Name:"));
2583 entry = gtk_entry_new();
2585 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2586 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2587 gtk_widget_show_all ( label );
2588 gtk_widget_show_all ( entry );
2590 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2592 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2594 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2595 // Find *first* wp with the given name
2596 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2599 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2602 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2603 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2605 // Find and select on the side panel
2610 // Hmmm, want key of it
2611 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2613 if ( wpf && udata.uuid ) {
2614 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2615 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2624 gtk_widget_destroy ( dia );
2627 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2629 gchar *default_name = highest_wp_number_get(vtl);
2630 VikWaypoint *wp = vik_waypoint_new();
2631 gchar *returned_name;
2633 wp->coord = *def_coord;
2635 // Attempt to auto set height if DEM data is available
2636 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2637 if ( elev != VIK_DEM_INVALID_ELEVATION )
2638 wp->altitude = (gdouble)elev;
2640 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2642 if ( returned_name )
2645 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2646 g_free (default_name);
2647 g_free (returned_name);
2650 g_free (default_name);
2651 vik_waypoint_free(wp);
2655 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2658 struct LatLon one_ll, two_ll;
2659 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2661 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2662 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2663 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2664 VikViewport *vvp = vik_window_viewport(vw);
2665 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2666 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2667 vik_coord_to_latlon(&one, &one_ll);
2668 vik_coord_to_latlon(&two, &two_ll);
2669 if (one_ll.lat > two_ll.lat) {
2670 maxmin[0].lat = one_ll.lat;
2671 maxmin[1].lat = two_ll.lat;
2674 maxmin[0].lat = two_ll.lat;
2675 maxmin[1].lat = one_ll.lat;
2677 if (one_ll.lon > two_ll.lon) {
2678 maxmin[0].lon = one_ll.lon;
2679 maxmin[1].lon = two_ll.lon;
2682 maxmin[0].lon = two_ll.lon;
2683 maxmin[1].lon = one_ll.lon;
2685 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2688 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2690 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2691 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2692 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2694 trw_layer_find_maxmin (vtl, maxmin);
2695 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2698 #ifdef VIK_CONFIG_GEOTAG
2699 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2701 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2703 // Update directly - not changing the mtime
2704 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2707 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2709 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2712 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2716 * Use code in separate file for this feature as reasonably complex
2718 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2720 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2721 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2722 // Unset so can be reverified later if necessary
2723 vtl->has_verified_thumbnails = FALSE;
2725 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2731 static void trw_layer_geotagging ( gpointer lav[2] )
2733 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2734 // Unset so can be reverified later if necessary
2735 vtl->has_verified_thumbnails = FALSE;
2737 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2744 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2747 * Acquire into this TRW Layer straight from GPS Device
2749 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2751 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2752 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2753 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2754 VikViewport *vvp = vik_window_viewport(vw);
2756 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2757 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2760 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2762 * Acquire into this TRW Layer from Google Directions
2764 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2766 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2767 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2768 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2769 VikViewport *vvp = vik_window_viewport(vw);
2771 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2775 #ifdef VIK_CONFIG_OPENSTREETMAP
2777 * Acquire into this TRW Layer from OSM
2779 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2781 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2782 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2783 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2784 VikViewport *vvp = vik_window_viewport(vw);
2786 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2790 #ifdef VIK_CONFIG_GEOCACHES
2792 * Acquire into this TRW Layer from Geocaching.com
2794 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2796 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2797 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2798 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2799 VikViewport *vvp = vik_window_viewport(vw);
2801 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2805 #ifdef VIK_CONFIG_GEOTAG
2807 * Acquire into this TRW Layer from images
2809 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2811 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2812 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2813 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2814 VikViewport *vvp = vik_window_viewport(vw);
2816 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2817 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2819 // Reverify thumbnails as they may have changed
2820 vtl->has_verified_thumbnails = FALSE;
2821 trw_layer_verify_thumbnails ( vtl, NULL );
2825 static void trw_layer_gps_upload ( gpointer lav[2] )
2827 gpointer pass_along[6];
2828 pass_along[0] = lav[0];
2829 pass_along[1] = lav[1];
2830 pass_along[2] = NULL; // No track - operate on the layer
2831 pass_along[3] = NULL;
2832 pass_along[4] = NULL;
2833 pass_along[5] = NULL;
2835 trw_layer_gps_upload_any ( pass_along );
2839 * If pass_along[3] is defined that this will upload just that track
2841 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
2843 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2844 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
2846 // May not actually get a track here as pass_along[3] can be null
2847 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2849 gboolean on_track = track ? TRUE : FALSE;
2851 if (on_track && !track->visible) {
2852 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
2856 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
2857 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2858 GTK_DIALOG_DESTROY_WITH_PARENT,
2860 GTK_RESPONSE_ACCEPT,
2862 GTK_RESPONSE_REJECT,
2865 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2866 GtkWidget *response_w = NULL;
2867 #if GTK_CHECK_VERSION (2, 20, 0)
2868 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2872 gtk_widget_grab_focus ( response_w );
2874 gpointer dgs = datasource_gps_setup ( dialog, on_track );
2876 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2877 datasource_gps_clean_up ( dgs );
2878 gtk_widget_destroy ( dialog );
2882 // Get info from reused datasource dialog widgets
2883 gchar* protocol = datasource_gps_get_protocol ( dgs );
2884 gchar* port = datasource_gps_get_descriptor ( dgs );
2885 // NB don't free the above strings as they're references to values held elsewhere
2886 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
2887 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
2888 gboolean turn_off = datasource_gps_get_off ( dgs );
2890 gtk_widget_destroy ( dialog );
2892 // When called from the viewport - work the corresponding layerspanel:
2894 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
2897 // Apply settings to transfer to the GPS device
2904 vik_layers_panel_get_viewport (vlp),
2912 * Acquire into this TRW Layer from any GPS Babel supported file
2914 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
2916 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2917 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2918 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2919 VikViewport *vvp = vik_window_viewport(vw);
2921 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
2924 static void trw_layer_new_wp ( gpointer lav[2] )
2926 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2927 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2928 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2929 instead return true if you want to update. */
2930 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 )
2931 vik_layers_panel_emit_update ( vlp );
2934 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2936 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2937 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2939 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2940 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2941 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2942 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2943 vik_layers_panel_emit_update ( vlp );
2947 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2949 /* NB do not care if wp is visible or not */
2950 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2953 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2955 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2956 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2958 /* Only 1 waypoint - jump straight to it */
2959 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2960 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2961 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2963 /* If at least 2 waypoints - find center and then zoom to fit */
2964 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2966 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2967 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2968 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2971 vik_layers_panel_emit_update ( vlp );
2974 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2976 static gpointer pass_along[2];
2978 GtkWidget *export_submenu;
2979 pass_along[0] = vtl;
2980 pass_along[1] = vlp;
2982 item = gtk_menu_item_new();
2983 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2984 gtk_widget_show ( item );
2986 /* Now with icons */
2987 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2988 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2990 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2991 gtk_widget_show ( item );
2993 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2995 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2996 gtk_widget_show ( item );
2998 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3000 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3001 gtk_widget_show ( item );
3003 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3004 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3006 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3007 gtk_widget_show ( item );
3009 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3010 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3011 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3012 gtk_widget_show ( item );
3014 export_submenu = gtk_menu_new ();
3015 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3016 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3017 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3018 gtk_widget_show ( item );
3019 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3021 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3023 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3024 gtk_widget_show ( item );
3026 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3028 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3029 gtk_widget_show ( item );
3031 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3033 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3034 gtk_widget_show ( item );
3036 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3038 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3039 gtk_widget_show ( item );
3041 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3042 item = gtk_menu_item_new_with_mnemonic ( external1 );
3043 g_free ( external1 );
3044 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3045 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3046 gtk_widget_show ( item );
3048 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3049 item = gtk_menu_item_new_with_mnemonic ( external2 );
3050 g_free ( external2 );
3051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3052 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3053 gtk_widget_show ( item );
3055 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3056 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3058 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3059 gtk_widget_show ( item );
3061 #ifdef VIK_CONFIG_GEONAMES
3062 GtkWidget *wikipedia_submenu = gtk_menu_new();
3063 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3064 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3065 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3066 gtk_widget_show(item);
3067 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3069 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3070 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3072 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3073 gtk_widget_show ( item );
3075 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3076 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3078 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3079 gtk_widget_show ( item );
3082 #ifdef VIK_CONFIG_GEOTAG
3083 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3084 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3085 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3086 gtk_widget_show ( item );
3089 GtkWidget *acquire_submenu = gtk_menu_new ();
3090 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3091 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3092 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3093 gtk_widget_show ( item );
3094 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3096 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3097 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3098 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3099 gtk_widget_show ( item );
3101 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
3102 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3104 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3105 gtk_widget_show ( item );
3108 #ifdef VIK_CONFIG_OPENSTREETMAP
3109 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3110 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3111 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3112 gtk_widget_show ( item );
3115 #ifdef VIK_CONFIG_GEOCACHES
3116 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3117 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3118 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3119 gtk_widget_show ( item );
3122 #ifdef VIK_CONFIG_GEOTAG
3123 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3124 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3125 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3126 gtk_widget_show ( item );
3129 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3131 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3132 gtk_widget_show ( item );
3134 GtkWidget *upload_submenu = gtk_menu_new ();
3135 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3136 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3137 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3138 gtk_widget_show ( item );
3139 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3141 #ifdef VIK_CONFIG_OPENSTREETMAP
3142 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3145 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3146 gtk_widget_show ( item );
3149 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3150 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3151 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3152 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3153 gtk_widget_show ( item );
3155 GtkWidget *delete_submenu = gtk_menu_new ();
3156 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3158 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3159 gtk_widget_show ( item );
3160 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3162 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3165 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3166 gtk_widget_show ( item );
3168 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3171 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3172 gtk_widget_show ( item );
3174 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3177 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3178 gtk_widget_show ( item );
3180 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3183 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3184 gtk_widget_show ( item );
3186 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3187 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3189 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3190 gtk_widget_show ( item );
3193 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3194 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3196 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3197 gtk_widget_show ( item );
3201 // Fake Waypoint UUIDs vi simple increasing integer
3202 static guint wp_uuid = 0;
3204 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3208 vik_waypoint_set_name (wp, name);
3210 if ( VIK_LAYER(vtl)->realized )
3212 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3214 // Visibility column always needed for waypoints
3215 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3216 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 );
3218 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 );
3220 // Actual setting of visibility dependent on the waypoint
3221 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3223 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3226 highest_wp_number_add_wp(vtl, name);
3227 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3231 // Fake Track UUIDs vi simple increasing integer
3232 static guint tr_uuid = 0;
3234 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3238 vik_track_set_name (t, name);
3240 if ( VIK_LAYER(vtl)->realized )
3242 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3243 // Visibility column always needed for tracks
3244 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3245 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 );
3247 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 );
3249 // Actual setting of visibility dependent on the track
3250 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3252 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3255 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3259 /* to be called whenever a track has been deleted or may have been changed. */
3260 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3262 if (vtl->current_tp_track == trk )
3263 trw_layer_cancel_current_tp ( vtl, FALSE );
3266 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3269 gchar *newname = g_strdup(name);
3270 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
3271 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
3272 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3274 newname = new_newname;
3280 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3282 // No more uniqueness of name forced when loading from a file
3283 // This now makes this function a little redunant as we just flow the parameters through
3284 vik_trw_layer_add_waypoint ( vtl, name, wp );
3287 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3289 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3290 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3291 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3292 vik_track_free ( tr );
3293 vtl->route_finder_append = FALSE; /* this means we have added it */
3296 // No more uniqueness of name forced when loading from a file
3297 vik_trw_layer_add_track ( vtl, name, tr );
3299 if ( vtl->route_finder_check_added_track ) {
3300 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3301 vtl->route_finder_added_track = tr;
3306 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3308 *l = g_list_append(*l, id);
3312 * Move an item from one TRW layer to another TRW layer
3314 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3316 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3317 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3319 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3321 VikTrack *trk2 = vik_track_copy ( trk );
3322 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3323 vik_trw_layer_delete_track ( vtl_src, trk );
3326 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3327 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3329 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3331 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3332 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3333 trw_layer_delete_waypoint ( vtl_src, wp );
3337 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3339 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3340 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3342 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3343 GList *items = NULL;
3346 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3347 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3349 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3350 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3355 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3356 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3358 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3365 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3366 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3371 VikTrack *trk; // input
3372 gpointer uuid; // output
3375 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3377 trku_udata *user_data = udata;
3378 if ( trk == user_data->trk ) {
3379 user_data->uuid = id;
3385 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3387 gboolean was_visible = FALSE;
3389 if ( trk && trk->name ) {
3391 if ( trk == vtl->current_track ) {
3392 vtl->current_track = NULL;
3393 vtl->current_tp_track = NULL;
3394 vtl->current_tp_id = NULL;
3395 vtl->moving_tp = FALSE;
3398 was_visible = trk->visible;
3400 if ( trk == vtl->route_finder_current_track )
3401 vtl->route_finder_current_track = NULL;
3403 if ( trk == vtl->route_finder_added_track )
3404 vtl->route_finder_added_track = NULL;
3410 // Hmmm, want key of it
3411 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3413 if ( trkf && udata.uuid ) {
3414 /* could be current_tp, so we have to check */
3415 trw_layer_cancel_tps_of_track ( vtl, trk );
3417 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3420 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3421 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3422 g_hash_table_remove ( vtl->tracks, udata.uuid );
3429 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3431 gboolean was_visible = FALSE;
3433 if ( wp && wp->name ) {
3435 if ( wp == vtl->current_wp ) {
3436 vtl->current_wp = NULL;
3437 vtl->current_wp_id = NULL;
3438 vtl->moving_wp = FALSE;
3441 was_visible = wp->visible;
3447 // Hmmm, want key of it
3448 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3450 if ( wpf && udata.uuid ) {
3451 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3454 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3455 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3457 highest_wp_number_remove_wp(vtl, wp->name);
3458 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3467 // Only for temporary use by trw_layer_delete_waypoint_by_name
3468 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3470 wpu_udata *user_data = udata;
3471 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3472 user_data->uuid = id;
3479 * Delete a waypoint by the given name
3480 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3481 * as there be multiple waypoints with the same name
3483 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3486 // Fake a waypoint with the given name
3487 udata.wp = vik_waypoint_new ();
3488 vik_waypoint_set_name (udata.wp, name);
3489 // Currently only the name is used in this waypoint find function
3492 // Hmmm, want key of it
3493 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3495 vik_waypoint_free (udata.wp);
3497 if ( wpf && udata.uuid )
3498 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3504 VikTrack *trk; // input
3505 gpointer uuid; // output
3508 // Only for temporary use by trw_layer_delete_track_by_name
3509 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3511 tpu_udata *user_data = udata;
3512 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3513 user_data->uuid = id;
3520 * Delete a track by the given name
3521 * NOTE: ATM this will delete the first encountered Track with the specified name
3522 * as there be multiple track with the same name
3524 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3527 // Fake a track with the given name
3528 udata.trk = vik_track_new ();
3529 vik_track_set_name (udata.trk, name);
3530 // Currently only the name is used in this waypoint find function
3533 // Hmmm, want key of it
3534 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3536 vik_track_free (udata.trk);
3538 if ( trkf && udata.uuid )
3539 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3544 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3546 vik_treeview_item_delete (vt, it );
3549 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3552 vtl->current_track = NULL;
3553 vtl->route_finder_current_track = NULL;
3554 vtl->route_finder_added_track = NULL;
3555 if (vtl->current_tp_track)
3556 trw_layer_cancel_current_tp(vtl, FALSE);
3558 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3559 g_hash_table_remove_all(vtl->tracks_iters);
3560 g_hash_table_remove_all(vtl->tracks);
3562 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3565 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3567 vtl->current_wp = NULL;
3568 vtl->current_wp_id = NULL;
3569 vtl->moving_wp = FALSE;
3571 highest_wp_number_reset(vtl);
3573 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3574 g_hash_table_remove_all(vtl->waypoints_iters);
3575 g_hash_table_remove_all(vtl->waypoints);
3577 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3580 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3582 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3583 // Get confirmation from the user
3584 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3585 _("Are you sure you want to delete all tracks in %s?"),
3586 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3587 vik_trw_layer_delete_all_tracks (vtl);
3590 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3592 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3593 // Get confirmation from the user
3594 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3595 _("Are you sure you want to delete all waypoints in %s?"),
3596 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3597 vik_trw_layer_delete_all_waypoints (vtl);
3600 static void trw_layer_delete_item ( gpointer pass_along[6] )
3602 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3603 gboolean was_visible = FALSE;
3604 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3606 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3607 if ( wp && wp->name ) {
3608 if ( GPOINTER_TO_INT ( pass_along[4]) )
3609 // Get confirmation from the user
3610 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3611 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3612 _("Are you sure you want to delete the waypoint \"%s\""),
3615 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3620 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3621 if ( trk && trk->name ) {
3622 if ( GPOINTER_TO_INT ( pass_along[4]) )
3623 // Get confirmation from the user
3624 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3625 _("Are you sure you want to delete the track \"%s\""),
3628 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3632 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3636 static void trw_layer_properties_item ( gpointer pass_along[7] )
3638 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3639 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3641 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3643 if ( wp && wp->name )
3645 gboolean updated = FALSE;
3646 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3648 if ( updated && VIK_LAYER(vtl)->visible )
3649 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3654 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3655 if ( tr && tr->name )
3657 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3659 pass_along[1], /* vlp */
3660 pass_along[5] ); /* vvp */
3666 Parameter 1 -> VikLayersPanel
3667 Parameter 2 -> VikLayer
3668 Parameter 3 -> VikViewport
3670 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3673 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3674 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3677 /* since vlp not set, vl & vvp should be valid instead! */
3679 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3680 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3685 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3687 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3688 if ( trps && trps->data )
3689 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3692 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3694 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3695 if ( track && track->trackpoints )
3697 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3699 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
3700 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3701 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3702 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3703 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3707 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3709 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3710 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3712 vtl->current_track = track;
3713 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3715 if ( track->trackpoints )
3716 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3719 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
3721 * extend a track using route finder
3723 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3725 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3726 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3727 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3729 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3730 vtl->route_finder_coord = last_coord;
3731 vtl->route_finder_current_track = track;
3732 vtl->route_finder_started = TRUE;
3734 if ( track->trackpoints )
3735 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3740 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3742 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3743 /* Also warn if overwrite old elevation data */
3744 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3746 vik_track_apply_dem_data ( track );
3749 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3751 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3754 trps = g_list_last(trps);
3755 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3758 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3760 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3763 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3766 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3768 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3771 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3774 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3776 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3779 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3783 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3785 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3787 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3788 if ( trk && trk->trackpoints )
3790 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3791 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
3792 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3793 if ( pass_along[1] )
3794 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3796 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3800 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3802 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3803 trw_layer_tpwin_init ( vtl );
3806 /*************************************
3807 * merge/split by time routines
3808 *************************************/
3810 /* called for each key in track hash table.
3811 * If the current track has the same time stamp type, add it to the result,
3812 * except the one pointed by "exclude".
3813 * set exclude to NULL if there is no exclude to check.
3814 * Note that the result is in reverse (for performance reasons).
3819 gboolean with_timestamps;
3821 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3823 twt_udata *user_data = udata;
3824 VikTrackpoint *p1, *p2;
3826 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3830 if (VIK_TRACK(value)->trackpoints) {
3831 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3832 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3834 if ( user_data->with_timestamps ) {
3835 if (!p1->has_timestamp || !p2->has_timestamp) {
3840 // Don't add tracks with timestamps when getting non timestamp tracks
3841 if (p1->has_timestamp || p2->has_timestamp) {
3847 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3850 /* called for each key in track hash table. if original track user_data[1] is close enough
3851 * to the passed one, add it to list in user_data[0]
3853 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
3856 VikTrackpoint *p1, *p2;
3857 VikTrack *trk = VIK_TRACK(value);
3859 GList **nearby_tracks = ((gpointer *)user_data)[0];
3860 GList *tpoints = ((gpointer *)user_data)[1];
3863 * detect reasons for not merging, and return
3864 * if no reason is found not to merge, then do it.
3867 // Exclude the original track from the compiled list
3868 if (trk->trackpoints == tpoints) {
3872 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
3873 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
3875 if (trk->trackpoints) {
3876 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
3877 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
3879 if (!p1->has_timestamp || !p2->has_timestamp) {
3880 //g_print("no timestamp\n");
3884 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3885 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
3886 if (! (abs(t1 - p2->timestamp) < threshold ||
3888 abs(p1->timestamp - t2) < threshold)
3895 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
3898 /* comparison function used to sort tracks; a and b are hash table keys */
3899 /* Not actively used - can be restored if needed
3900 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3902 GHashTable *tracks = user_data;
3905 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3906 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3908 if (t1 < t2) return -1;
3909 if (t1 > t2) return 1;
3914 /* comparison function used to sort trackpoints */
3915 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3917 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3919 if (t1 < t2) return -1;
3920 if (t1 > t2) return 1;
3925 * comparison function which can be used to sort tracks or waypoints by name
3927 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3929 const gchar* namea = (const gchar*) a;
3930 const gchar* nameb = (const gchar*) b;
3931 if ( namea == NULL || nameb == NULL)
3934 // Same sort method as used in the vik_treeview_*_alphabetize functions
3935 return strcmp ( namea, nameb );
3939 * Attempt to merge selected track with other tracks specified by the user
3940 * Tracks to merge with must be of the same 'type' as the selected track -
3941 * either all with timestamps, or all without timestamps
3943 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3945 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3946 GList *other_tracks = NULL;
3947 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3949 if ( !track->trackpoints )
3953 udata.result = &other_tracks;
3954 udata.exclude = track->trackpoints;
3955 // Allow merging with 'similar' time type time tracks
3956 // i.e. either those times, or those without
3957 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3959 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3960 other_tracks = g_list_reverse(other_tracks);
3962 if ( !other_tracks ) {
3963 if ( udata.with_timestamps )
3964 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3966 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3970 // Sort alphabetically for user presentation
3971 // Convert into list of names for usage with dialog function
3972 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3973 GList *other_tracks_names = NULL;
3974 GList *iter = g_list_first ( other_tracks );
3976 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
3977 iter = g_list_next ( iter );
3980 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
3982 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3983 other_tracks_names, TRUE,
3984 _("Merge with..."), _("Select track to merge with"));
3985 g_list_free(other_tracks);
3986 g_list_free(other_tracks_names);
3991 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3992 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
3994 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3995 merge_track->trackpoints = NULL;
3996 vik_trw_layer_delete_track (vtl, merge_track);
3997 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4000 /* TODO: free data before free merge_list */
4001 for (l = merge_list; l != NULL; l = g_list_next(l))
4003 g_list_free(merge_list);
4004 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4008 // c.f. trw_layer_sorted_track_id_by_name_list
4009 // but don't add the specified track to the list (normally current track)
4010 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4012 twt_udata *user_data = udata;
4015 if (trk->trackpoints == user_data->exclude) {
4019 // Sort named list alphabetically
4020 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4024 * Join - this allows combining 'routes' and 'tracks'
4025 * i.e. doesn't care about whether tracks have consistent timestamps
4026 * ATM can only append one track at a time to the currently selected track
4028 static void trw_layer_append_track ( gpointer pass_along[6] )
4031 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4032 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4034 GList *other_tracks_names = NULL;
4036 // Sort alphabetically for user presentation
4037 // Convert into list of names for usage with dialog function
4038 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4040 udata.result = &other_tracks_names;
4041 udata.exclude = trk->trackpoints;
4043 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4045 // Note the limit to selecting one track only
4046 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4047 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4048 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4052 _("Select the track to append after the current track"));
4054 g_list_free(other_tracks_names);
4056 // It's a list, but shouldn't contain more than one other track!
4057 if ( append_list ) {
4059 for (l = append_list; l != NULL; l = g_list_next(l)) {
4060 // TODO: at present this uses the first track found by name,
4061 // which with potential multiple same named tracks may not be the one selected...
4062 VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data );
4063 if ( append_track ) {
4064 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4065 append_track->trackpoints = NULL;
4066 vik_trw_layer_delete_track (vtl, append_track);
4069 for (l = append_list; l != NULL; l = g_list_next(l))
4071 g_list_free(append_list);
4072 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4076 /* merge by segments */
4077 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4079 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4080 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4081 guint segments = vik_track_merge_segments ( trk );
4082 // NB currently no need to redraw as segments not actually shown on the display
4083 // However inform the user of what happened:
4085 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4086 g_snprintf(str, 64, tmp_str, segments);
4087 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4090 /* merge by time routine */
4091 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4093 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4097 GList *tracks_with_timestamp = NULL;
4098 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4099 if (orig_trk->trackpoints &&
4100 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4101 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4106 udata.result = &tracks_with_timestamp;
4107 udata.exclude = orig_trk->trackpoints;
4108 udata.with_timestamps = TRUE;
4109 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4110 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4112 if (!tracks_with_timestamp) {
4113 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4116 g_list_free(tracks_with_timestamp);
4118 static guint threshold_in_minutes = 1;
4119 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4120 _("Merge Threshold..."),
4121 _("Merge when time between tracks less than:"),
4122 &threshold_in_minutes)) {
4126 // keep attempting to merge all tracks until no merges within the time specified is possible
4127 gboolean attempt_merge = TRUE;
4128 GList *nearby_tracks = NULL;
4130 static gpointer params[3];
4132 while ( attempt_merge ) {
4134 // Don't try again unless tracks have changed
4135 attempt_merge = FALSE;
4137 trps = orig_trk->trackpoints;
4141 if (nearby_tracks) {
4142 g_list_free(nearby_tracks);
4143 nearby_tracks = NULL;
4146 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4147 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4149 /* g_print("Original track times: %d and %d\n", t1, t2); */
4150 params[0] = &nearby_tracks;
4151 params[1] = (gpointer)trps;
4152 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4154 /* get a list of adjacent-in-time tracks */
4155 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4158 GList *l = nearby_tracks;
4161 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4162 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4164 t1 = get_first_trackpoint(l)->timestamp;
4165 t2 = get_last_trackpoint(l)->timestamp;
4166 #undef get_first_trackpoint
4167 #undef get_last_trackpoint
4168 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4171 /* remove trackpoints from merged track, delete track */
4172 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4173 VIK_TRACK(l->data)->trackpoints = NULL;
4174 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4176 // Tracks have changed, therefore retry again against all the remaining tracks
4177 attempt_merge = TRUE;
4182 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
4185 g_list_free(nearby_tracks);
4186 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4190 * Split a track at the currently selected trackpoint
4192 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl )
4194 if ( !vtl->current_tpl )
4197 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
4198 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4200 VikTrack *tr = vik_track_new ();
4201 GList *newglist = g_list_alloc ();
4202 newglist->prev = NULL;
4203 newglist->next = vtl->current_tpl->next;
4204 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4205 tr->trackpoints = newglist;
4207 vtl->current_tpl->next->prev = newglist; /* end old track here */
4208 vtl->current_tpl->next = NULL;
4210 vtl->current_tpl = newglist; /* change tp to first of new track. */
4211 vtl->current_tp_track = tr;
4215 vik_trw_layer_add_track ( vtl, name, tr );
4221 // Also need id of newly created track
4222 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4223 if ( trkf && udata.uuid )
4224 vtl->current_tp_id = udata.uuid;
4226 vtl->current_tp_id = NULL;
4228 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4233 /* split by time routine */
4234 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
4236 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4237 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4238 GList *trps = track->trackpoints;
4240 GList *newlists = NULL;
4241 GList *newtps = NULL;
4242 static guint thr = 1;
4249 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4250 _("Split Threshold..."),
4251 _("Split when time between trackpoints exceeds:"),
4256 /* iterate through trackpoints, and copy them into new lists without touching original list */
4257 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
4261 ts = VIK_TRACKPOINT(iter->data)->timestamp;
4263 g_print("panic: ts < prev_ts: this should never happen!\n");
4266 if (ts - prev_ts > thr*60) {
4267 /* flush accumulated trackpoints into new list */
4268 newlists = g_list_append(newlists, g_list_reverse(newtps));
4272 /* accumulate trackpoint copies in newtps, in reverse order */
4273 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4275 iter = g_list_next(iter);
4278 newlists = g_list_append(newlists, g_list_reverse(newtps));
4281 /* put lists of trackpoints into tracks */
4283 // Only bother updating if the split results in new tracks
4284 if (g_list_length (newlists) > 1) {
4289 tr = vik_track_new();
4290 tr->visible = track->visible;
4291 tr->trackpoints = (GList *)(iter->data);
4293 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4294 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4295 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
4296 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
4298 iter = g_list_next(iter);
4300 // Remove original track and then update the display
4301 vik_trw_layer_delete_track (vtl, track);
4302 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4304 g_list_free(newlists);
4308 * Split a track by the number of points as specified by the user
4310 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
4312 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4313 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4315 // Check valid track
4316 GList *trps = track->trackpoints;
4320 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4321 _("Split Every Nth Point"),
4322 _("Split on every Nth point:"),
4323 250, // Default value as per typical limited track capacity of various GPS devices
4327 // Was a valid number returned?
4333 GList *newlists = NULL;
4334 GList *newtps = NULL;
4339 /* accumulate trackpoint copies in newtps, in reverse order */
4340 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4342 if (count >= points) {
4343 /* flush accumulated trackpoints into new list */
4344 newlists = g_list_append(newlists, g_list_reverse(newtps));
4348 iter = g_list_next(iter);
4351 // If there is a remaining chunk put that into the new split list
4352 // This may well be the whole track if no split points were encountered
4354 newlists = g_list_append(newlists, g_list_reverse(newtps));
4357 /* put lists of trackpoints into tracks */
4359 // Only bother updating if the split results in new tracks
4360 if (g_list_length (newlists) > 1) {
4365 tr = vik_track_new();
4366 tr->visible = track->visible;
4367 tr->trackpoints = (GList *)(iter->data);
4369 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4370 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4372 iter = g_list_next(iter);
4374 // Remove original track and then update the display
4375 vik_trw_layer_delete_track (vtl, track);
4376 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4378 g_list_free(newlists);
4382 * Split a track at the currently selected trackpoint
4384 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
4386 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4387 trw_layer_split_at_selected_trackpoint ( vtl );
4391 * Split a track by its segments
4393 static void trw_layer_split_segments ( gpointer pass_along[6] )
4395 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4396 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4399 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
4402 for ( i = 0; i < ntracks; i++ ) {
4404 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
4405 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
4410 // Remove original track
4411 vik_trw_layer_delete_track ( vtl, trk );
4412 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4415 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
4418 /* end of split/merge routines */
4421 * Delete adjacent track points at the same position
4422 * AKA Delete Dulplicates on the Properties Window
4424 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
4426 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4427 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4429 gulong removed = vik_track_remove_dup_points ( trk );
4431 // Track has been updated so update tps:
4432 trw_layer_cancel_tps_of_track ( vtl, trk );
4434 // Inform user how much was deleted as it's not obvious from the normal view
4436 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4437 g_snprintf(str, 64, tmp_str, removed);
4438 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4440 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4444 * Delete adjacent track points with the same timestamp
4445 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
4447 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
4449 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4450 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4452 gulong removed = vik_track_remove_same_time_points ( trk );
4454 // Track has been updated so update tps:
4455 trw_layer_cancel_tps_of_track ( vtl, trk );
4457 // Inform user how much was deleted as it's not obvious from the normal view
4459 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4460 g_snprintf(str, 64, tmp_str, removed);
4461 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4463 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4469 static void trw_layer_reverse ( gpointer pass_along[6] )
4471 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4472 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4474 // Check valid track
4475 GList *trps = track->trackpoints;
4479 vik_track_reverse ( track );
4481 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4485 * Similar to trw_layer_enum_item, but this uses a sorted method
4488 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4490 GList **list = (GList**)udata;
4491 // *list = g_list_prepend(*all, key); //unsorted method
4492 // Sort named list alphabetically
4493 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4498 * Now Waypoint specific sort
4500 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4502 GList **list = (GList**)udata;
4503 // Sort named list alphabetically
4504 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4508 * Track specific sort
4510 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4512 GList **list = (GList**)udata;
4513 // Sort named list alphabetically
4514 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4519 gboolean has_same_track_name;
4520 const gchar *same_track_name;
4521 } same_track_name_udata;
4523 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4525 const gchar* namea = (const gchar*) aa;
4526 const gchar* nameb = (const gchar*) bb;
4529 gint result = strcmp ( namea, nameb );
4531 if ( result == 0 ) {
4532 // Found two names the same
4533 same_track_name_udata *user_data = udata;
4534 user_data->has_same_track_name = TRUE;
4535 user_data->same_track_name = namea;
4538 // Leave ordering the same
4543 * Find out if any tracks have the same name in this layer
4545 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4547 // Sort items by name, then compare if any next to each other are the same
4549 GList *track_names = NULL;
4550 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4553 if ( ! track_names )
4556 same_track_name_udata udata;
4557 udata.has_same_track_name = FALSE;
4559 // Use sort routine to traverse list comparing items
4560 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4561 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4562 // Still no tracks...
4566 return udata.has_same_track_name;
4570 * Force unqiue track names for this layer
4571 * Note the panel is a required parameter to enable the update of the names displayed
4573 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4575 // . Search list for an instance of repeated name
4576 // . get track of this name
4577 // . create new name
4578 // . rename track & update equiv. treeview iter
4579 // . repeat until all different
4581 same_track_name_udata udata;
4583 GList *track_names = NULL;
4584 udata.has_same_track_name = FALSE;
4585 udata.same_track_name = NULL;
4587 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4590 if ( ! track_names )
4593 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4595 // Still no tracks...
4596 if ( ! dummy_list1 )
4599 while ( udata.has_same_track_name ) {
4601 // Find a track with the same name
4602 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4606 g_critical("Houston, we've had a problem.");
4607 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4608 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4613 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4614 vik_track_set_name ( trk, newname );
4620 // Need want key of it for treeview update
4621 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4623 if ( trkf && udataU.uuid ) {
4625 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4628 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4629 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4630 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4635 // Start trying to find same names again...
4637 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4638 udata.has_same_track_name = FALSE;
4639 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4641 // No tracks any more - give up searching
4642 if ( ! dummy_list2 )
4643 udata.has_same_track_name = FALSE;
4647 vik_layers_panel_emit_update ( vlp );
4653 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4655 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4658 // Ensure list of track names offered is unique
4659 if ( trw_layer_has_same_track_names ( vtl ) ) {
4660 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4661 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4662 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4668 // Sort list alphabetically for better presentation
4669 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4672 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4676 // Get list of items to delete from the user
4677 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4680 _("Delete Selection"),
4681 _("Select tracks to delete"));
4684 // Delete requested tracks
4685 // since specificly requested, IMHO no need for extra confirmation
4686 if ( delete_list ) {
4688 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4689 // This deletes first trk it finds of that name (but uniqueness is enforced above)
4690 trw_layer_delete_track_by_name (vtl, l->data);
4692 g_list_free(delete_list);
4693 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4698 gboolean has_same_waypoint_name;
4699 const gchar *same_waypoint_name;
4700 } same_waypoint_name_udata;
4702 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4704 const gchar* namea = (const gchar*) aa;
4705 const gchar* nameb = (const gchar*) bb;
4708 gint result = strcmp ( namea, nameb );
4710 if ( result == 0 ) {
4711 // Found two names the same
4712 same_waypoint_name_udata *user_data = udata;
4713 user_data->has_same_waypoint_name = TRUE;
4714 user_data->same_waypoint_name = namea;
4717 // Leave ordering the same
4722 * Find out if any waypoints have the same name in this layer
4724 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
4726 // Sort items by name, then compare if any next to each other are the same
4728 GList *waypoint_names = NULL;
4729 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4732 if ( ! waypoint_names )
4735 same_waypoint_name_udata udata;
4736 udata.has_same_waypoint_name = FALSE;
4738 // Use sort routine to traverse list comparing items
4739 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4740 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4741 // Still no waypoints...
4745 return udata.has_same_waypoint_name;
4749 * Force unqiue waypoint names for this layer
4750 * Note the panel is a required parameter to enable the update of the names displayed
4752 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4754 // . Search list for an instance of repeated name
4755 // . get waypoint of this name
4756 // . create new name
4757 // . rename waypoint & update equiv. treeview iter
4758 // . repeat until all different
4760 same_waypoint_name_udata udata;
4762 GList *waypoint_names = NULL;
4763 udata.has_same_waypoint_name = FALSE;
4764 udata.same_waypoint_name = NULL;
4766 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4769 if ( ! waypoint_names )
4772 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4774 // Still no waypoints...
4775 if ( ! dummy_list1 )
4778 while ( udata.has_same_waypoint_name ) {
4780 // Find a waypoint with the same name
4781 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
4785 g_critical("Houston, we've had a problem.");
4786 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4787 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
4792 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
4793 vik_waypoint_set_name ( waypoint, newname );
4796 udataU.wp = waypoint;
4799 // Need want key of it for treeview update
4800 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4802 if ( wpf && udataU.uuid ) {
4804 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4807 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4808 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4809 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4814 // Start trying to find same names again...
4815 waypoint_names = NULL;
4816 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4817 udata.has_same_waypoint_name = FALSE;
4818 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4820 // No waypoints any more - give up searching
4821 if ( ! dummy_list2 )
4822 udata.has_same_waypoint_name = FALSE;
4826 vik_layers_panel_emit_update ( vlp );
4832 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4834 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4837 // Ensure list of waypoint names offered is unique
4838 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
4839 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4840 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4841 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4847 // Sort list alphabetically for better presentation
4848 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4850 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4854 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4856 // Get list of items to delete from the user
4857 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4860 _("Delete Selection"),
4861 _("Select waypoints to delete"));
4864 // Delete requested waypoints
4865 // since specificly requested, IMHO no need for extra confirmation
4866 if ( delete_list ) {
4868 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4869 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
4870 trw_layer_delete_waypoint_by_name (vtl, l->data);
4872 g_list_free(delete_list);
4873 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4878 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4880 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4882 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4885 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4887 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4888 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4892 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4894 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4896 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
4898 // No actual change to the name supplied
4899 if (strcmp(newname, wp->name) == 0 )
4902 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4905 // An existing waypoint has been found with the requested name
4906 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4907 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
4912 // Update WP name and refresh the treeview
4913 vik_waypoint_set_name (wp, newname);
4915 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4916 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4919 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4924 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4926 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
4928 // No actual change to the name supplied
4929 if (strcmp(newname, trk->name) == 0)
4932 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4935 // An existing track has been found with the requested name
4936 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4937 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4941 // Update track name and refresh GUI parts
4942 vik_track_set_name (trk, newname);
4944 // Update any subwindows that could be displaying this track which has changed name
4945 // Only one Track Edit Window
4946 if ( l->current_tp_track == trk && l->tpwin ) {
4947 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
4949 // Property Dialog of the track
4950 vik_trw_layer_propwin_update ( trk );
4952 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4953 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4956 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4963 static gboolean is_valid_geocache_name ( gchar *str )
4965 gint len = strlen ( str );
4966 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]));
4969 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
4971 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4972 a_acquire_set_filter_track ( trk );
4975 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
4976 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
4978 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
4979 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
4982 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
4984 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4986 gchar *escaped = uri_escape ( tr->comment );
4987 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4988 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4995 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4996 /* viewpoint is now available instead */
4997 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4999 static gpointer pass_along[8];
5001 gboolean rv = FALSE;
5004 pass_along[1] = vlp;
5005 pass_along[2] = GINT_TO_POINTER (subtype);
5006 pass_along[3] = sublayer;
5007 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5008 pass_along[5] = vvp;
5009 pass_along[6] = iter;
5010 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
5012 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5016 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5017 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5018 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5019 gtk_widget_show ( item );
5021 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5022 VikTrwLayer *vtl = l;
5023 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
5024 if (tr && tr->property_dialog)
5025 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5028 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5030 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5031 gtk_widget_show ( item );
5033 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
5034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
5035 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5036 gtk_widget_show ( item );
5038 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
5039 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
5040 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5041 gtk_widget_show ( item );
5043 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5045 gboolean separator_created = FALSE;
5047 /* could be a right-click using the tool */
5048 if ( vlp != NULL ) {
5049 item = gtk_menu_item_new ();
5050 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5051 gtk_widget_show ( item );
5053 separator_created = TRUE;
5055 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5056 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
5058 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5059 gtk_widget_show ( item );
5062 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5064 if ( wp && wp->name ) {
5065 if ( is_valid_geocache_name ( wp->name ) ) {
5067 if ( !separator_created ) {
5068 item = gtk_menu_item_new ();
5069 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5070 gtk_widget_show ( item );
5071 separator_created = TRUE;
5074 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
5075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
5076 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5077 gtk_widget_show ( item );
5081 if ( wp && wp->image )
5083 if ( !separator_created ) {
5084 item = gtk_menu_item_new ();
5085 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5086 gtk_widget_show ( item );
5087 separator_created = TRUE;
5090 // Set up image paramater
5091 pass_along[5] = wp->image;
5093 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
5094 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
5095 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
5096 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5097 gtk_widget_show ( item );
5099 #ifdef VIK_CONFIG_GEOTAG
5100 GtkWidget *geotag_submenu = gtk_menu_new ();
5101 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
5102 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
5103 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5104 gtk_widget_show ( item );
5105 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
5107 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
5108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
5109 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5110 gtk_widget_show ( item );
5112 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
5113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
5114 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5115 gtk_widget_show ( item );
5122 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
5125 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
5126 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
5128 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5129 gtk_widget_show ( item );
5132 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
5134 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
5135 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
5137 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5138 gtk_widget_show ( item );
5140 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
5141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
5143 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5144 gtk_widget_show ( item );
5146 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
5147 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5148 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
5149 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5150 gtk_widget_show ( item );
5152 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
5153 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
5155 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5156 gtk_widget_show ( item );
5159 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
5163 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
5164 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5165 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
5166 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5167 gtk_widget_show ( item );
5169 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
5170 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
5172 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5173 gtk_widget_show ( item );
5175 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
5176 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5177 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
5178 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5179 gtk_widget_show ( item );
5182 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5184 item = gtk_menu_item_new ();
5185 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5186 gtk_widget_show ( item );
5188 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
5189 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5190 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
5191 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5192 gtk_widget_show ( item );
5194 GtkWidget *goto_submenu;
5195 goto_submenu = gtk_menu_new ();
5196 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5197 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5198 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5199 gtk_widget_show ( item );
5200 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
5202 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
5203 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
5204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
5205 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5206 gtk_widget_show ( item );
5208 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
5209 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5210 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
5211 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5212 gtk_widget_show ( item );
5214 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
5215 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
5216 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
5217 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5218 gtk_widget_show ( item );
5220 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
5221 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
5222 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
5223 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5224 gtk_widget_show ( item );
5226 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
5227 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
5228 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
5229 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5230 gtk_widget_show ( item );
5232 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
5233 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
5234 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
5235 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5236 gtk_widget_show ( item );
5238 GtkWidget *combine_submenu;
5239 combine_submenu = gtk_menu_new ();
5240 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
5241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
5242 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5243 gtk_widget_show ( item );
5244 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
5246 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
5247 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
5248 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5249 gtk_widget_show ( item );
5251 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
5252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
5253 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5254 gtk_widget_show ( item );
5256 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
5257 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
5258 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5259 gtk_widget_show ( item );
5261 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
5262 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
5263 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5264 gtk_widget_show ( item );
5266 GtkWidget *split_submenu;
5267 split_submenu = gtk_menu_new ();
5268 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
5269 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
5270 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5271 gtk_widget_show ( item );
5272 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
5274 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
5275 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
5276 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5277 gtk_widget_show ( item );
5279 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
5280 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
5281 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5282 gtk_widget_show ( item );
5284 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
5285 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
5286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
5287 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5288 gtk_widget_show ( item );
5290 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
5291 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
5292 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5293 gtk_widget_show ( item );
5294 // Make it available only when a trackpoint is selected.
5295 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
5297 GtkWidget *delete_submenu;
5298 delete_submenu = gtk_menu_new ();
5299 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
5300 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
5301 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5302 gtk_widget_show ( item );
5303 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
5305 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
5306 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
5307 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5308 gtk_widget_show ( item );
5310 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
5311 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
5312 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5313 gtk_widget_show ( item );
5315 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
5316 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
5317 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
5318 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5319 gtk_widget_show ( item );
5321 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
5323 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
5324 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
5325 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
5326 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5327 gtk_widget_show ( item );
5330 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
5331 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
5332 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
5333 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5334 gtk_widget_show ( item );
5336 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
5337 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
5338 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
5339 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5340 gtk_widget_show ( item );
5342 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
5343 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
5344 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
5345 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5346 gtk_widget_show ( item );
5348 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
5349 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
5350 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
5351 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
5352 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5353 gtk_widget_show ( item );
5356 GtkWidget *upload_submenu = gtk_menu_new ();
5357 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
5358 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5359 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5360 gtk_widget_show ( item );
5361 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
5363 #ifdef VIK_CONFIG_OPENSTREETMAP
5364 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
5365 // Convert internal pointer into actual track for usage outside this file
5366 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
5367 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
5369 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5370 gtk_widget_show ( item );
5373 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
5374 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
5375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
5376 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5377 gtk_widget_show ( item );
5379 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
5380 if ( is_valid_google_route ( l, sublayer ) )
5382 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
5383 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
5384 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
5385 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5386 gtk_widget_show ( item );
5390 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
5391 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5392 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
5393 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5394 gtk_widget_show ( item );
5396 /* ATM This function is only available via the layers panel, due to needing a vlp */
5398 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
5399 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
5400 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
5402 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5403 gtk_widget_show ( item );
5407 #ifdef VIK_CONFIG_GEOTAG
5408 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
5409 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
5410 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5411 gtk_widget_show ( item );
5414 // Only show on viewport popmenu when a trackpoint is selected
5415 if ( ! vlp && l->current_tpl ) {
5417 item = gtk_menu_item_new ();
5418 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5419 gtk_widget_show ( item );
5421 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
5422 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
5423 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
5424 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5425 gtk_widget_show ( item );
5433 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
5436 if (!vtl->current_tpl)
5438 if (!vtl->current_tpl->next)
5441 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
5442 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
5444 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
5447 VikTrackpoint *tp_new = vik_trackpoint_new();
5448 struct LatLon ll_current, ll_next;
5449 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
5450 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
5452 /* main positional interpolation */
5453 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
5454 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
5456 /* Now other properties that can be interpolated */
5457 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
5459 if (tp_current->has_timestamp && tp_next->has_timestamp) {
5460 /* Note here the division is applied to each part, then added
5461 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
5462 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
5463 tp_new->has_timestamp = TRUE;
5466 if (tp_current->speed != NAN && tp_next->speed != NAN)
5467 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
5469 /* TODO - improve interpolation of course, as it may not be correct.
5470 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
5471 [similar applies if value is in radians] */
5472 if (tp_current->course != NAN && tp_next->course != NAN)
5473 tp_new->speed = (tp_current->course + tp_next->course)/2;
5475 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
5477 /* Insert new point into the trackpoints list after the current TP */
5478 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5479 gint index = g_list_index ( tr->trackpoints, tp_current );
5481 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
5486 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
5492 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
5496 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
5498 if ( vtl->current_tpl )
5500 vtl->current_tpl = NULL;
5501 vtl->current_tp_track = NULL;
5502 vtl->current_tp_id = NULL;
5503 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5507 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
5509 g_assert ( vtl->tpwin != NULL );
5510 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
5511 trw_layer_cancel_current_tp ( vtl, TRUE );
5513 if ( vtl->current_tpl == NULL )
5516 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
5518 trw_layer_split_at_selected_trackpoint ( vtl );
5519 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5521 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5523 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5529 // Find available adjacent trackpoint
5530 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5532 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5533 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5535 // Delete current trackpoint
5536 vik_trackpoint_free ( vtl->current_tpl->data );
5537 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5539 // Set to current to the available adjacent trackpoint
5540 vtl->current_tpl = new_tpl;
5542 // Reset dialog with the available adjacent trackpoint
5543 if ( vtl->current_tp_track )
5544 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
5546 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5550 // Delete current trackpoint
5551 vik_trackpoint_free ( vtl->current_tpl->data );
5552 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5553 trw_layer_cancel_current_tp ( vtl, FALSE );
5556 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5558 if ( vtl->current_tp_track )
5559 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
5560 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
5562 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5564 if ( vtl->current_tp_track )
5565 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
5566 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5568 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5570 trw_layer_insert_tp_after_current_tp ( vtl );
5571 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5573 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
5574 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5577 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5581 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5582 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5583 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5584 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5585 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5587 if ( vtl->current_tpl )
5588 if ( vtl->current_tp_track )
5589 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5590 /* set layer name and TP data */
5593 /***************************************************************************
5595 ***************************************************************************/
5597 /*** Utility data structures and functions ****/
5601 gint closest_x, closest_y;
5602 gpointer *closest_wp_id;
5603 VikWaypoint *closest_wp;
5609 gint closest_x, closest_y;
5610 gpointer closest_track_id;
5611 VikTrackpoint *closest_tp;
5616 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5622 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5624 // If waypoint has an image then use the image size to select
5626 gint slackx, slacky;
5627 slackx = wp->image_width / 2;
5628 slacky = wp->image_height / 2;
5630 if ( x <= params->x + slackx && x >= params->x - slackx
5631 && y <= params->y + slacky && y >= params->y - slacky ) {
5632 params->closest_wp_id = id;
5633 params->closest_wp = wp;
5634 params->closest_x = x;
5635 params->closest_y = y;
5638 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5639 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5640 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5642 params->closest_wp_id = id;
5643 params->closest_wp = wp;
5644 params->closest_x = x;
5645 params->closest_y = y;
5649 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5651 GList *tpl = t->trackpoints;
5660 tp = VIK_TRACKPOINT(tpl->data);
5662 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5664 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5665 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5666 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5668 params->closest_track_id = id;
5669 params->closest_tp = tp;
5670 params->closest_tpl = tpl;
5671 params->closest_x = x;
5672 params->closest_y = y;
5678 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5680 TPSearchParams params;
5684 params.closest_track_id = NULL;
5685 params.closest_tp = NULL;
5686 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5687 return params.closest_tp;
5690 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5692 WPSearchParams params;
5696 params.closest_wp = NULL;
5697 params.closest_wp_id = NULL;
5698 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5699 return params.closest_wp;
5703 // Some forward declarations
5704 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5705 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5706 static void marker_end_move ( tool_ed_t *t );
5709 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5713 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5715 // Here always allow snapping back to the original location
5716 // this is useful when one decides not to move the thing afterall
5717 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5720 if ( event->state & GDK_CONTROL_MASK )
5722 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5724 new_coord = tp->coord;
5728 if ( event->state & GDK_SHIFT_MASK )
5730 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5732 new_coord = wp->coord;
5736 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5738 marker_moveto ( t, x, y );
5745 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5747 if ( t->holding && event->button == 1 )
5750 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5753 if ( event->state & GDK_CONTROL_MASK )
5755 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5757 new_coord = tp->coord;
5761 if ( event->state & GDK_SHIFT_MASK )
5763 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5765 new_coord = wp->coord;
5768 marker_end_move ( t );
5770 // Determine if working on a waypoint or a trackpoint
5771 if ( t->is_waypoint )
5772 vtl->current_wp->coord = new_coord;
5774 if ( vtl->current_tpl ) {
5775 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5778 if ( vtl->current_tp_track )
5779 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5784 vtl->current_wp = NULL;
5785 vtl->current_wp_id = NULL;
5786 trw_layer_cancel_current_tp ( vtl, FALSE );
5788 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5795 Returns true if a waypoint or track is found near the requested event position for this particular layer
5796 The item found is automatically selected
5797 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5799 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5801 if ( event->button != 1 )
5804 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5807 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5810 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5812 if (vtl->waypoints_visible) {
5813 WPSearchParams wp_params;
5814 wp_params.vvp = vvp;
5815 wp_params.x = event->x;
5816 wp_params.y = event->y;
5817 wp_params.closest_wp_id = NULL;
5818 wp_params.closest_wp = NULL;
5820 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5822 if ( wp_params.closest_wp ) {
5825 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5827 // Too easy to move it so must be holding shift to start immediately moving it
5828 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5829 if ( event->state & GDK_SHIFT_MASK ||
5830 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5831 // Put into 'move buffer'
5832 // NB vvp & vw already set in tet
5833 tet->vtl = (gpointer)vtl;
5834 tet->is_waypoint = TRUE;
5836 marker_begin_move (tet, event->x, event->y);
5839 vtl->current_wp = wp_params.closest_wp;
5840 vtl->current_wp_id = wp_params.closest_wp_id;
5842 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5848 if (vtl->tracks_visible) {
5849 TPSearchParams tp_params;
5850 tp_params.vvp = vvp;
5851 tp_params.x = event->x;
5852 tp_params.y = event->y;
5853 tp_params.closest_track_id = NULL;
5854 tp_params.closest_tp = NULL;
5856 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5858 if ( tp_params.closest_tp ) {
5860 // Always select + highlight the track
5861 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5863 tet->is_waypoint = FALSE;
5865 // Select the Trackpoint
5866 // Can move it immediately when control held or it's the previously selected tp
5867 if ( event->state & GDK_CONTROL_MASK ||
5868 vtl->current_tpl == tp_params.closest_tpl ) {
5869 // Put into 'move buffer'
5870 // NB vvp & vw already set in tet
5871 tet->vtl = (gpointer)vtl;
5872 marker_begin_move (tet, event->x, event->y);
5875 vtl->current_tpl = tp_params.closest_tpl;
5876 vtl->current_tp_id = tp_params.closest_track_id;
5877 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5879 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5882 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5884 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5889 /* these aren't the droids you're looking for */
5890 vtl->current_wp = NULL;
5891 vtl->current_wp_id = NULL;
5892 trw_layer_cancel_current_tp ( vtl, FALSE );
5895 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5900 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5902 if ( event->button != 3 )
5905 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5908 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5911 /* Post menu for the currently selected item */
5913 /* See if a track is selected */
5914 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5915 if ( track && track->visible ) {
5917 if ( track->name ) {
5919 if ( vtl->track_right_click_menu )
5920 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5922 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5928 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5930 if ( trkf && udataU.uuid ) {
5932 GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5934 trw_layer_sublayer_add_menu_items ( vtl,
5935 vtl->track_right_click_menu,
5937 VIK_TRW_LAYER_SUBLAYER_TRACK,
5943 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5949 /* See if a waypoint is selected */
5950 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5951 if ( waypoint && waypoint->visible ) {
5952 if ( waypoint->name ) {
5954 if ( vtl->wp_right_click_menu )
5955 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
5957 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5960 udata.wp = waypoint;
5963 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
5965 if ( wpf && udata.uuid ) {
5966 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
5968 trw_layer_sublayer_add_menu_items ( vtl,
5969 vtl->wp_right_click_menu,
5971 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5976 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5985 /* background drawing hook, to be passed the viewport */
5986 static gboolean tool_sync_done = TRUE;
5988 static gboolean tool_sync(gpointer data)
5990 VikViewport *vvp = data;
5991 gdk_threads_enter();
5992 vik_viewport_sync(vvp);
5993 tool_sync_done = TRUE;
5994 gdk_threads_leave();
5998 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
6001 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
6002 gdk_gc_set_function ( t->gc, GDK_INVERT );
6003 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6004 vik_viewport_sync(t->vvp);
6009 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
6011 VikViewport *vvp = t->vvp;
6012 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6013 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6017 if (tool_sync_done) {
6018 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
6019 tool_sync_done = FALSE;
6023 static void marker_end_move ( tool_ed_t *t )
6025 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6026 g_object_unref ( t->gc );
6030 /*** Edit waypoint ****/
6032 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6034 tool_ed_t *t = g_new(tool_ed_t, 1);
6040 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6042 WPSearchParams params;
6043 tool_ed_t *t = data;
6044 VikViewport *vvp = t->vvp;
6046 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6053 if ( !vtl->vl.visible || !vtl->waypoints_visible )
6056 if ( vtl->current_wp && vtl->current_wp->visible )
6058 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
6060 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
6062 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
6063 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
6065 if ( event->button == 3 )
6066 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6068 marker_begin_move(t, event->x, event->y);
6075 params.x = event->x;
6076 params.y = event->y;
6077 params.closest_wp_id = NULL;
6078 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6079 params.closest_wp = NULL;
6080 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6081 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
6083 // how do we get here?
6084 marker_begin_move(t, event->x, event->y);
6085 g_critical("shouldn't be here");
6088 else if ( params.closest_wp )
6090 if ( event->button == 3 )
6091 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6093 vtl->waypoint_rightclick = FALSE;
6095 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
6097 vtl->current_wp = params.closest_wp;
6098 vtl->current_wp_id = params.closest_wp_id;
6100 /* could make it so don't update if old WP is off screen and new is null but oh well */
6101 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6105 vtl->current_wp = NULL;
6106 vtl->current_wp_id = NULL;
6107 vtl->waypoint_rightclick = FALSE;
6108 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6112 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6114 tool_ed_t *t = data;
6115 VikViewport *vvp = t->vvp;
6117 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6122 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6125 if ( event->state & GDK_CONTROL_MASK )
6127 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6129 new_coord = tp->coord;
6133 if ( event->state & GDK_SHIFT_MASK )
6135 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6136 if ( wp && wp != vtl->current_wp )
6137 new_coord = wp->coord;
6142 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6144 marker_moveto ( t, x, y );
6151 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6153 tool_ed_t *t = data;
6154 VikViewport *vvp = t->vvp;
6156 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6159 if ( t->holding && event->button == 1 )
6162 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6165 if ( event->state & GDK_CONTROL_MASK )
6167 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6169 new_coord = tp->coord;
6173 if ( event->state & GDK_SHIFT_MASK )
6175 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6176 if ( wp && wp != vtl->current_wp )
6177 new_coord = wp->coord;
6180 marker_end_move ( t );
6182 vtl->current_wp->coord = new_coord;
6183 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6186 /* PUT IN RIGHT PLACE!!! */
6187 if ( event->button == 3 && vtl->waypoint_rightclick )
6189 if ( vtl->wp_right_click_menu )
6190 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
6191 if ( vtl->current_wp ) {
6192 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
6193 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 );
6194 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6196 vtl->waypoint_rightclick = FALSE;
6201 /**** Begin track ***/
6202 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
6207 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6209 vtl->current_track = NULL;
6210 return tool_new_track_click ( vtl, event, vvp );
6213 /*** New track ****/
6215 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
6222 GdkDrawable *drawable;
6228 * Draw specified pixmap
6230 static gboolean draw_sync ( gpointer data )
6232 draw_sync_t *ds = (draw_sync_t*) data;
6233 // Sometimes don't want to draw
6234 // normally because another update has taken precedent such as panning the display
6235 // which means this pixmap is no longer valid
6236 if ( ds->vtl->draw_sync_do ) {
6237 gdk_threads_enter();
6238 gdk_draw_drawable (ds->drawable,
6241 0, 0, 0, 0, -1, -1);
6242 ds->vtl->draw_sync_done = TRUE;
6243 gdk_threads_leave();
6248 static const gchar* distance_string (gdouble distance)
6252 /* draw label with distance */
6253 vik_units_distance_t dist_units = a_vik_get_units_distance ();
6254 switch (dist_units) {
6255 case VIK_UNITS_DISTANCE_MILES:
6256 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
6257 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
6258 } else if (distance < 1609.4) {
6259 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
6261 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
6265 // VIK_UNITS_DISTANCE_KILOMETRES
6266 if (distance >= 1000 && distance < 100000) {
6267 g_sprintf(str, "%3.2f km", distance/1000.0);
6268 } else if (distance < 1000) {
6269 g_sprintf(str, "%d m", (int)distance);
6271 g_sprintf(str, "%d km", (int)distance/1000);
6275 return g_strdup (str);
6279 * Actually set the message in statusbar
6281 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
6283 // Only show elevation data when track has some elevation properties
6284 gchar str_gain_loss[64];
6285 str_gain_loss[0] = '\0';
6287 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
6288 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
6289 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
6291 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
6294 // Write with full gain/loss information
6295 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
6296 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
6301 * Figure out what information should be set in the statusbar and then write it
6303 static void update_statusbar ( VikTrwLayer *vtl )
6305 // Get elevation data
6306 gdouble elev_gain, elev_loss;
6307 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6309 /* Find out actual distance of current track */
6310 gdouble distance = vik_track_get_length (vtl->current_track);
6311 const gchar *str = distance_string (distance);
6313 statusbar_write (str, elev_gain, elev_loss, vtl);
6315 g_free ((gpointer)str);
6319 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
6321 /* if we haven't sync'ed yet, we don't have time to do more. */
6322 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
6323 GList *iter = g_list_last ( vtl->current_track->trackpoints );
6325 static GdkPixmap *pixmap = NULL;
6327 // Need to check in case window has been resized
6328 w1 = vik_viewport_get_width(vvp);
6329 h1 = vik_viewport_get_height(vvp);
6331 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6333 gdk_drawable_get_size (pixmap, &w2, &h2);
6334 if (w1 != w2 || h1 != h2) {
6335 g_object_unref ( G_OBJECT ( pixmap ) );
6336 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6339 // Reset to background
6340 gdk_draw_drawable (pixmap,
6341 vtl->current_track_newpoint_gc,
6342 vik_viewport_get_pixmap(vvp),
6343 0, 0, 0, 0, -1, -1);
6345 draw_sync_t *passalong;
6348 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
6350 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
6351 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
6352 // thus when we come to reset to the background it would include what we have already drawn!!
6353 gdk_draw_line ( pixmap,
6354 vtl->current_track_newpoint_gc,
6355 x1, y1, event->x, event->y );
6356 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
6358 /* Find out actual distance of current track */
6359 gdouble distance = vik_track_get_length (vtl->current_track);
6361 // Now add distance to where the pointer is //
6364 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
6365 vik_coord_to_latlon ( &coord, &ll );
6366 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
6368 // Get elevation data
6369 gdouble elev_gain, elev_loss;
6370 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6372 // Adjust elevation data (if available) for the current pointer position
6374 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
6375 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
6376 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
6377 // Adjust elevation of last track point
6378 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
6380 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
6383 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
6387 const gchar *str = distance_string (distance);
6389 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
6390 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
6392 pango_layout_set_text (pl, str, -1);
6394 pango_layout_get_pixel_size ( pl, &wd, &hd );
6397 // offset from cursor a bit depending on font size
6401 // Create a background block to make the text easier to read over the background map
6402 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
6403 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
6404 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
6406 g_object_unref ( G_OBJECT ( pl ) );
6407 g_object_unref ( G_OBJECT ( background_block_gc ) );
6409 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
6410 passalong->vtl = vtl;
6411 passalong->pixmap = pixmap;
6412 passalong->drawable = GTK_WIDGET(vvp)->window;
6413 passalong->gc = vtl->current_track_newpoint_gc;
6415 // Update statusbar with full gain/loss information
6416 statusbar_write (str, elev_gain, elev_loss, vtl);
6418 g_free ((gpointer)str);
6420 // draw pixmap when we have time to
6421 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
6422 vtl->draw_sync_done = FALSE;
6423 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
6425 return VIK_LAYER_TOOL_ACK;
6428 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
6430 if ( vtl->current_track && event->keyval == GDK_Escape ) {
6431 vtl->current_track = NULL;
6432 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6434 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
6436 if ( vtl->current_track->trackpoints )
6438 GList *last = g_list_last(vtl->current_track->trackpoints);
6439 g_free ( last->data );
6440 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6443 update_statusbar ( vtl );
6445 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6451 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6455 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6458 if ( event->button == 2 ) {
6459 // As the display is panning, the new track pixmap is now invalid so don't draw it
6460 // otherwise this drawing done results in flickering back to an old image
6461 vtl->draw_sync_do = FALSE;
6465 if ( event->button == 3 && vtl->current_track )
6468 if ( vtl->current_track->trackpoints )
6470 GList *last = g_list_last(vtl->current_track->trackpoints);
6471 g_free ( last->data );
6472 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6474 update_statusbar ( vtl );
6476 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6480 if ( event->type == GDK_2BUTTON_PRESS )
6482 /* subtract last (duplicate from double click) tp then end */
6483 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
6485 GList *last = g_list_last(vtl->current_track->trackpoints);
6486 g_free ( last->data );
6487 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6488 /* undo last, then end */
6489 vtl->current_track = NULL;
6491 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6495 if ( ! vtl->current_track )
6497 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
6498 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
6500 vtl->current_track = vik_track_new();
6501 vtl->current_track->visible = TRUE;
6502 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
6504 /* incase it was created by begin track */
6505 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
6510 tp = vik_trackpoint_new();
6511 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
6513 /* snap to other TP */
6514 if ( event->state & GDK_CONTROL_MASK )
6516 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6518 tp->coord = other_tp->coord;
6521 tp->newsegment = FALSE;
6522 tp->has_timestamp = FALSE;
6524 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
6525 /* Auto attempt to get elevation from DEM data (if it's available) */
6526 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
6528 vtl->ct_x1 = vtl->ct_x2;
6529 vtl->ct_y1 = vtl->ct_y2;
6530 vtl->ct_x2 = event->x;
6531 vtl->ct_y2 = event->y;
6533 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6537 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6539 if ( event->button == 2 ) {
6540 // Pan moving ended - enable potential point drawing again
6541 vtl->draw_sync_do = TRUE;
6542 vtl->draw_sync_done = TRUE;
6546 /*** New waypoint ****/
6548 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6553 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6556 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6558 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6559 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
6560 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6565 /*** Edit trackpoint ****/
6567 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6569 tool_ed_t *t = g_new(tool_ed_t, 1);
6575 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6577 tool_ed_t *t = data;
6578 VikViewport *vvp = t->vvp;
6579 TPSearchParams params;
6580 /* OUTDATED DOCUMENTATION:
6581 find 5 pixel range on each side. then put these UTM, and a pointer
6582 to the winning track name (and maybe the winning track itself), and a
6583 pointer to the winning trackpoint, inside an array or struct. pass
6584 this along, do a foreach on the tracks which will do a foreach on the
6587 params.x = event->x;
6588 params.y = event->y;
6589 params.closest_track_id = NULL;
6590 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6591 params.closest_tp = NULL;
6593 if ( event->button != 1 )
6596 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6599 if ( !vtl->vl.visible || !vtl->tracks_visible )
6602 if ( vtl->current_tpl )
6604 /* 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.) */
6605 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6606 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6611 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6613 if ( current_tr->visible &&
6614 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
6615 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6616 marker_begin_move ( t, event->x, event->y );
6622 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6624 if ( params.closest_tp )
6626 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
6627 vtl->current_tpl = params.closest_tpl;
6628 vtl->current_tp_id = params.closest_track_id;
6629 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
6630 trw_layer_tpwin_init ( vtl );
6631 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
6632 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6636 /* these aren't the droids you're looking for */
6640 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6642 tool_ed_t *t = data;
6643 VikViewport *vvp = t->vvp;
6645 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6651 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6654 if ( event->state & GDK_CONTROL_MASK )
6656 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6657 if ( tp && tp != vtl->current_tpl->data )
6658 new_coord = tp->coord;
6660 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6663 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6664 marker_moveto ( t, x, y );
6672 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6674 tool_ed_t *t = data;
6675 VikViewport *vvp = t->vvp;
6677 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6679 if ( event->button != 1)
6684 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6687 if ( event->state & GDK_CONTROL_MASK )
6689 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6690 if ( tp && tp != vtl->current_tpl->data )
6691 new_coord = tp->coord;
6694 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6696 marker_end_move ( t );
6698 /* diff dist is diff from orig */
6700 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6702 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6709 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
6710 /*** Route Finder ***/
6711 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6716 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6719 if ( !vtl ) return FALSE;
6720 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6721 if ( event->button == 3 && vtl->route_finder_current_track ) {
6723 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6725 vtl->route_finder_coord = *new_end;
6727 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6728 /* remove last ' to:...' */
6729 if ( vtl->route_finder_current_track->comment ) {
6730 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6731 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6732 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6733 last_to - vtl->route_finder_current_track->comment - 1);
6734 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6739 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6740 struct LatLon start, end;
6741 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6742 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6745 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6746 vik_coord_to_latlon ( &(tmp), &end );
6747 vtl->route_finder_coord = tmp; /* for continuations */
6749 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6750 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6751 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6753 vtl->route_finder_check_added_track = TRUE;
6754 vtl->route_finder_started = FALSE;
6757 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6758 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6759 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6760 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6761 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6762 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
6765 /* see if anything was done -- a track was added or appended to */
6766 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6767 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 ) );
6768 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6769 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6770 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6771 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6773 vtl->route_finder_added_track = NULL;
6774 vtl->route_finder_check_added_track = FALSE;
6775 vtl->route_finder_append = FALSE;
6777 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6779 vtl->route_finder_started = TRUE;
6780 vtl->route_finder_coord = tmp;
6781 vtl->route_finder_current_track = NULL;
6787 /*** Show picture ****/
6789 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6794 /* Params are: vvp, event, last match found or NULL */
6795 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6797 if ( wp->image && wp->visible )
6799 gint x, y, slackx, slacky;
6800 GdkEventButton *event = (GdkEventButton *) params[1];
6802 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6803 slackx = wp->image_width / 2;
6804 slacky = wp->image_height / 2;
6805 if ( x <= event->x + slackx && x >= event->x - slackx
6806 && y <= event->y + slacky && y >= event->y - slacky )
6808 params[2] = wp->image; /* we've found a match. however continue searching
6809 * since we want to find the last match -- that
6810 * is, the match that was drawn last. */
6815 static void trw_layer_show_picture ( gpointer pass_along[6] )
6817 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6819 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
6822 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6823 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6824 g_free ( quoted_file );
6825 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6827 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() );
6828 g_error_free ( err );
6831 #endif /* WINDOWS */
6834 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6836 gpointer params[3] = { vvp, event, NULL };
6837 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6839 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6842 static gpointer pass_along[6];
6843 pass_along[0] = vtl;
6844 pass_along[5] = params[2];
6845 trw_layer_show_picture ( pass_along );
6846 return TRUE; /* found a match */
6849 return FALSE; /* go through other layers, searching for a match */
6852 /***************************************************************************
6854 ***************************************************************************/
6860 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6862 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6863 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6866 /* Structure for thumbnail creating data used in the background thread */
6868 VikTrwLayer *vtl; // Layer needed for redrawing
6869 GSList *pics; // Image list
6870 } thumbnail_create_thread_data;
6872 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6874 guint total = g_slist_length(tctd->pics), done = 0;
6875 while ( tctd->pics )
6877 a_thumbnails_create ( (gchar *) tctd->pics->data );
6878 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6880 return -1; /* Abort thread */
6882 tctd->pics = tctd->pics->next;
6885 // Redraw to show the thumbnails as they are now created
6886 if ( IS_VIK_LAYER(tctd->vtl) )
6887 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6892 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6894 while ( tctd->pics )
6896 g_free ( tctd->pics->data );
6897 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6902 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6904 if ( ! vtl->has_verified_thumbnails )
6906 GSList *pics = NULL;
6907 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6910 gint len = g_slist_length ( pics );
6911 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6912 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6915 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6917 (vik_thr_func) create_thumbnails_thread,
6919 (vik_thr_free_func) thumbnail_create_thread_free,
6927 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6929 return vtl->coord_mode;
6933 * Uniquify the whole layer
6934 * Also requires the layers panel as the names shown there need updating too
6935 * Returns whether the operation was successful or not
6937 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6940 vik_trw_layer_uniquify_tracks ( vtl, vlp );
6941 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
6947 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
6949 vik_coord_convert ( &(wp->coord), *dest_mode );
6952 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6954 vik_track_convert ( tr, *dest_mode );
6957 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6959 if ( vtl->coord_mode != dest_mode )
6961 vtl->coord_mode = dest_mode;
6962 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6963 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6967 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
6969 vtl->menu_selection = selection;
6972 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
6974 return (vtl->menu_selection);
6977 /* ----------- Downloading maps along tracks --------------- */
6979 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6981 /* TODO: calculating based on current size of viewport */
6982 const gdouble w_at_zoom_0_125 = 0.0013;
6983 const gdouble h_at_zoom_0_125 = 0.0011;
6984 gdouble zoom_factor = zoom_level/0.125;
6986 wh->lat = h_at_zoom_0_125 * zoom_factor;
6987 wh->lon = w_at_zoom_0_125 * zoom_factor;
6989 return 0; /* all OK */
6992 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6994 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6995 (dist->lat >= ABS(to->north_south - from->north_south)))
6998 VikCoord *coord = g_malloc(sizeof(VikCoord));
6999 coord->mode = VIK_COORD_LATLON;
7001 if (ABS(gradient) < 1) {
7002 if (from->east_west > to->east_west)
7003 coord->east_west = from->east_west - dist->lon;
7005 coord->east_west = from->east_west + dist->lon;
7006 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
7008 if (from->north_south > to->north_south)
7009 coord->north_south = from->north_south - dist->lat;
7011 coord->north_south = from->north_south + dist->lat;
7012 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
7018 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
7020 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
7021 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
7023 VikCoord *next = from;
7025 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
7027 list = g_list_prepend(list, next);
7033 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
7035 typedef struct _Rect {
7040 #define GLRECT(iter) ((Rect *)((iter)->data))
7043 GList *rects_to_download = NULL;
7046 if (get_download_area_width(vvp, zoom_level, &wh))
7049 GList *iter = tr->trackpoints;
7053 gboolean new_map = TRUE;
7054 VikCoord *cur_coord, tl, br;
7057 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
7059 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7060 rect = g_malloc(sizeof(Rect));
7063 rect->center = *cur_coord;
7064 rects_to_download = g_list_prepend(rects_to_download, rect);
7069 gboolean found = FALSE;
7070 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7071 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7082 GList *fillins = NULL;
7083 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
7084 /* seems that ATM the function get_next_coord works only for LATLON */
7085 if ( cur_coord->mode == VIK_COORD_LATLON ) {
7086 /* fill-ins for far apart points */
7087 GList *cur_rect, *next_rect;
7088 for (cur_rect = rects_to_download;
7089 (next_rect = cur_rect->next) != NULL;
7090 cur_rect = cur_rect->next) {
7091 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
7092 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
7093 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
7097 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
7100 GList *iter = fillins;
7102 cur_coord = (VikCoord *)(iter->data);
7103 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7104 rect = g_malloc(sizeof(Rect));
7107 rect->center = *cur_coord;
7108 rects_to_download = g_list_prepend(rects_to_download, rect);
7113 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7114 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7118 for (iter = fillins; iter; iter = iter->next)
7120 g_list_free(fillins);
7122 if (rects_to_download) {
7123 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
7124 g_free(rect_iter->data);
7125 g_list_free(rects_to_download);
7129 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7132 gint selected_map, default_map;
7133 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
7134 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
7135 gint selected_zoom, default_zoom;
7139 VikTrwLayer *vtl = pass_along[0];
7140 VikLayersPanel *vlp = pass_along[1];
7141 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7142 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
7144 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7145 int num_maps = g_list_length(vmls);
7148 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7152 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
7153 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
7155 gchar **np = map_names;
7156 VikMapsLayer **lp = map_layers;
7157 for (i = 0; i < num_maps; i++) {
7158 gboolean dup = FALSE;
7159 vml = (VikMapsLayer *)(vmls->data);
7160 for (j = 0; j < i; j++) { /* no duplicate allowed */
7161 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
7168 *np++ = vik_maps_layer_get_map_label(vml);
7174 num_maps = lp - map_layers;
7176 for (default_map = 0; default_map < num_maps; default_map++) {
7177 /* TODO: check for parent layer's visibility */
7178 if (VIK_LAYER(map_layers[default_map])->visible)
7181 default_map = (default_map == num_maps) ? 0 : default_map;
7183 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
7184 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
7185 if (cur_zoom == zoom_vals[default_zoom])
7188 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
7190 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
7193 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7196 for (i = 0; i < num_maps; i++)
7197 g_free(map_names[i]);
7205 /**** lowest waypoint number calculation ***/
7206 static gint highest_wp_number_name_to_number(const gchar *name) {
7207 if ( strlen(name) == 3 ) {
7209 if ( n < 100 && name[0] != '0' )
7211 if ( n < 10 && name[0] != '0' )
7219 static void highest_wp_number_reset(VikTrwLayer *vtl)
7221 vtl->highest_wp_number = -1;
7224 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
7226 /* if is bigger that top, add it */
7227 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
7228 if ( new_wp_num > vtl->highest_wp_number )
7229 vtl->highest_wp_number = new_wp_num;
7232 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
7234 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
7235 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
7236 if ( vtl->highest_wp_number == old_wp_num ) {
7238 vtl->highest_wp_number--;
7240 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7241 /* search down until we find something that *does* exist */
7243 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
7244 vtl->highest_wp_number--;
7245 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7250 /* get lowest unused number */
7251 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
7254 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
7256 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
7257 return g_strdup(buf);