2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
26 /* viktrwlayer.c -- 5000+ lines can make a difference in the state of things */
33 #include "vikmapslayer.h"
34 #include "vikgpslayer.h"
35 #include "viktrwlayer_tpwin.h"
36 #include "viktrwlayer_propwin.h"
37 #ifdef VIK_CONFIG_GEOTAG
38 #include "viktrwlayer_geotag.h"
39 #include "geotag_exif.h"
41 #include "garminsymbols.h"
42 #include "thumbnails.h"
43 #include "background.h"
48 #include "geonamessearch.h"
49 #ifdef VIK_CONFIG_OPENSTREETMAP
50 #include "osm-traces.h"
53 #include "datasources.h"
54 #include "datasource_gps.h"
57 #include "icons/icons.h"
71 #include <gdk/gdkkeysyms.h>
73 #include <glib/gstdio.h>
74 #include <glib/gi18n.h>
76 #ifdef VIK_CONFIG_GOOGLE
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
80 #define VIK_TRW_LAYER_TRACK_GC 16
81 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
82 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
83 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
84 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
85 #define VIK_TRW_LAYER_TRACK_GC_SLOW 13
86 #define VIK_TRW_LAYER_TRACK_GC_AVER 14
87 #define VIK_TRW_LAYER_TRACK_GC_FAST 15
89 #define DRAWMODE_BY_TRACK 0
90 #define DRAWMODE_BY_SPEED 1
91 #define DRAWMODE_ALL_BLACK 2
92 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
93 // as we are (re)calculating the colour for every point
98 /* this is how it knows when you click if you are clicking close to a trackpoint. */
99 #define TRACKPOINT_SIZE_APPROX 5
100 #define WAYPOINT_SIZE_APPROX 5
102 #define MIN_STOP_LENGTH 15
103 #define MAX_STOP_LENGTH 86400
104 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
105 /* this is multiplied by user-inputted value from 1-100. */
107 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
109 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
111 FS_XX_SMALL = 0, // 'xx-small'
114 FS_MEDIUM, // DEFAULT
121 struct _VikTrwLayer {
124 GHashTable *tracks_iters;
125 GHashTable *waypoints_iters;
126 GHashTable *waypoints;
127 GtkTreeIter waypoints_iter, tracks_iter;
128 gboolean tracks_visible, waypoints_visible;
131 guint8 drawpoints_size;
132 guint8 drawelevation;
133 guint8 elevation_factor;
137 guint8 drawdirections;
138 guint8 drawdirections_size;
139 guint8 line_thickness;
140 guint8 bg_line_thickness;
144 gboolean wp_draw_symbols;
145 font_size_t wp_font_size;
147 gdouble track_draw_speed_factor;
149 GdkGC *current_track_gc;
150 // Separate GC for a track's potential new point as drawn via separate method
151 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
152 GdkGC *current_track_newpoint_gc;
155 GdkGC *waypoint_text_gc;
156 GdkGC *waypoint_bg_gc;
157 VikTrack *current_track;
158 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
159 gboolean draw_sync_done;
160 gboolean draw_sync_do;
162 VikCoordMode coord_mode;
164 /* wp editing tool */
165 VikWaypoint *current_wp;
166 gpointer current_wp_id;
168 gboolean waypoint_rightclick;
170 /* track editing tool */
172 VikTrack *current_tp_track;
173 gpointer current_tp_id;
174 VikTrwLayerTpwin *tpwin;
176 /* track editing tool -- more specifically, moving tps */
179 /* route finder tool */
180 gboolean route_finder_started;
181 VikCoord route_finder_coord;
182 gboolean route_finder_check_added_track;
183 VikTrack *route_finder_added_track;
184 VikTrack *route_finder_current_track;
185 gboolean route_finder_append;
192 guint16 image_cache_size;
194 /* for waypoint text */
195 PangoLayout *wplabellayout;
197 gboolean has_verified_thumbnails;
199 GtkMenu *wp_right_click_menu;
200 GtkMenu *track_right_click_menu;
203 VikStdLayerMenuItem menu_selection;
205 gint highest_wp_number;
208 /* A caached waypoint image. */
211 gchar *image; /* filename */
214 struct DrawingParams {
218 guint16 width, height;
219 gdouble cc; // Cosine factor in track directions
220 gdouble ss; // Sine factor in track directions
221 const VikCoord *center;
223 gboolean one_zone, lat_lon;
224 gdouble ce1, ce2, cn1, cn2;
227 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
229 static void trw_layer_delete_item ( gpointer pass_along[6] );
230 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
231 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
233 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
234 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] );
235 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
237 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
238 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
240 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
241 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
243 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
244 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
245 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
246 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
247 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
248 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
249 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
250 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
251 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
252 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
253 static void trw_layer_append_track ( gpointer pass_along[6] );
254 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
255 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
256 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
257 static void trw_layer_split_segments ( gpointer pass_along[6] );
258 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
259 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
260 static void trw_layer_reverse ( gpointer pass_along[6] );
261 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
262 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
263 static void trw_layer_show_picture ( gpointer pass_along[6] );
264 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
266 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
267 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
268 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
269 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
270 static void trw_layer_new_wp ( gpointer lav[2] );
271 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
272 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
273 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
274 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
275 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
276 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
277 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
278 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
279 #ifdef VIK_CONFIG_GEOTAG
280 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
281 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
282 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
283 static void trw_layer_geotagging ( gpointer lav[2] );
285 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
286 #ifdef VIK_CONFIG_GOOGLE
287 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
289 #ifdef VIK_CONFIG_OPENSTREETMAP
290 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
292 #ifdef VIK_CONFIG_GEOCACHES
293 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
295 #ifdef VIK_CONFIG_GEOTAG
296 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
298 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
299 static void trw_layer_gps_upload ( gpointer lav[2] );
302 static void trw_layer_properties_item ( gpointer pass_along[7] );
303 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
304 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
306 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
307 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
308 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
310 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
311 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
312 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
313 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
315 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
316 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
317 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
318 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
319 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
320 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
321 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
322 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
323 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
324 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
325 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
326 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
327 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
328 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
329 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
330 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
331 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
332 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
333 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
334 #ifdef VIK_CONFIG_GOOGLE
335 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
336 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
339 static void cached_pixbuf_free ( CachedPixbuf *cp );
340 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
342 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
343 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
345 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
346 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
348 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
349 static void highest_wp_number_reset(VikTrwLayer *vtl);
350 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
351 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
353 // Note for the following tool GtkRadioActionEntry texts:
354 // the very first text value is an internal name not displayed anywhere
355 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
356 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
357 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
358 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
359 static VikToolInterface trw_layer_tools[] = {
360 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
361 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
362 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
364 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
366 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
367 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
368 (VikToolMouseFunc) tool_new_track_click,
369 (VikToolMouseMoveFunc) tool_new_track_move,
370 (VikToolMouseFunc) tool_new_track_release,
371 (VikToolKeyFunc) tool_new_track_key_press,
372 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
373 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
375 { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "<control><shift>B", N_("Begin Track"), 0 },
376 (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
377 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL,
379 GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
381 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
382 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
383 (VikToolMouseFunc) tool_edit_waypoint_click,
384 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
385 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
387 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
389 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
390 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
391 (VikToolMouseFunc) tool_edit_trackpoint_click,
392 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
393 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
395 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
397 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
398 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
399 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
401 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
403 #ifdef VIK_CONFIG_GOOGLE
404 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
405 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
406 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
408 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
411 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
413 /****** PARAMETERS ******/
415 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
416 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
418 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
419 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
421 #define MIN_POINT_SIZE 2
422 #define MAX_POINT_SIZE 10
424 #define MIN_ARROW_SIZE 3
425 #define MAX_ARROW_SIZE 20
427 static VikLayerParamScale params_scales[] = {
428 /* min max step digits */
429 { 1, 10, 1, 0 }, /* line_thickness */
430 { 0, 100, 1, 0 }, /* track draw speed factor */
431 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
432 /* 5 * step == how much to turn */
433 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
434 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
435 { 5, 500, 5, 0 }, // 5: image cache_size - " "
436 { 0, 8, 1, 0 }, // 6: Background line thickness
437 { 1, 64, 1, 0 }, /* wpsize */
438 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
439 { 1, 100, 1, 0 }, // 9: elevation factor
440 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
441 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
444 static gchar* params_font_sizes[] = {
445 N_("Extra Extra Small"),
451 N_("Extra Extra Large"),
454 VikLayerParam trw_layer_params[] = {
455 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
456 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
458 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
459 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
460 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
461 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
462 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
463 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
464 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
465 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
466 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
468 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
469 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
471 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
472 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
473 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
475 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
476 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
477 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
478 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
479 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
480 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
481 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
482 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
483 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
485 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
486 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
487 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
488 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
491 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
493 // Sublayer visibilities
530 *** 1) Add to trw_layer_params and enumeration
531 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
534 /****** END PARAMETERS ******/
536 static VikTrwLayer* trw_layer_new ( gint drawmode );
537 /* Layer Interface function definitions */
538 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
539 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
540 static void trw_layer_free ( VikTrwLayer *trwlayer );
541 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
542 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
543 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
544 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
545 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
546 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
547 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
548 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
549 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
550 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
551 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
552 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
553 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
554 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
555 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
556 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
557 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
558 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
559 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
560 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
561 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
562 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
563 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
564 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
565 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
566 /* End Layer Interface function definitions */
568 VikLayerInterface vik_trw_layer_interface = {
575 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
579 params_groups, /* params_groups */
580 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
584 (VikLayerFuncCreate) trw_layer_create,
585 (VikLayerFuncRealize) trw_layer_realize,
586 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
587 (VikLayerFuncFree) trw_layer_free,
589 (VikLayerFuncProperties) NULL,
590 (VikLayerFuncDraw) trw_layer_draw,
591 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
593 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
594 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
596 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
597 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
599 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
600 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
601 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
602 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
603 (VikLayerFuncLayerSelected) trw_layer_selected,
605 (VikLayerFuncMarshall) trw_layer_marshall,
606 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
608 (VikLayerFuncSetParam) trw_layer_set_param,
609 (VikLayerFuncGetParam) trw_layer_get_param,
611 (VikLayerFuncReadFileData) a_gpspoint_read_file,
612 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
614 (VikLayerFuncDeleteItem) trw_layer_del_item,
615 (VikLayerFuncCutItem) trw_layer_cut_item,
616 (VikLayerFuncCopyItem) trw_layer_copy_item,
617 (VikLayerFuncPasteItem) trw_layer_paste_item,
618 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
620 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
622 (VikLayerFuncSelectClick) trw_layer_select_click,
623 (VikLayerFuncSelectMove) trw_layer_select_move,
624 (VikLayerFuncSelectRelease) trw_layer_select_release,
625 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
634 GType vik_trw_layer_get_type ()
636 static GType vtl_type = 0;
640 static const GTypeInfo vtl_info =
642 sizeof (VikTrwLayerClass),
643 NULL, /* base_init */
644 NULL, /* base_finalize */
645 NULL, /* class init */
646 NULL, /* class_finalize */
647 NULL, /* class_data */
648 sizeof (VikTrwLayer),
650 NULL /* instance init */
652 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
658 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
660 static gpointer pass_along[6];
666 pass_along[1] = NULL;
667 pass_along[2] = GINT_TO_POINTER (subtype);
668 pass_along[3] = sublayer;
669 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
670 pass_along[5] = NULL;
672 trw_layer_delete_item ( pass_along );
675 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
677 static gpointer pass_along[6];
683 pass_along[1] = NULL;
684 pass_along[2] = GINT_TO_POINTER (subtype);
685 pass_along[3] = sublayer;
686 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
687 pass_along[5] = NULL;
689 trw_layer_copy_item_cb(pass_along);
690 trw_layer_cut_item_cb(pass_along);
693 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
695 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
696 gint subtype = GPOINTER_TO_INT (pass_along[2]);
697 gpointer * sublayer = pass_along[3];
701 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
705 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
706 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
707 if ( wp && wp->name )
710 name = NULL; // Broken :(
713 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
714 if ( trk && trk->name )
717 name = NULL; // Broken :(
720 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
721 subtype, len, name, data);
725 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
727 trw_layer_copy_item_cb(pass_along);
728 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
729 trw_layer_delete_item(pass_along);
732 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
743 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
745 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
746 // 'Simple' memory copy of byte array from the marshalling above
747 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
748 fi = g_malloc ( *len );
750 memcpy(fi->data, id, il);
753 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
754 // less magic than before...
755 *len = sizeof(FlatItem) + 1 + il;
756 fi = g_malloc ( *len );
758 memcpy(fi->data, id, il);
762 *item = (guint8 *)fi;
765 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
767 FlatItem *fi = (FlatItem *) item;
769 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
774 w = vik_waypoint_unmarshall(fi->data, fi->len);
775 // When copying - we'll create a new name based on the original
776 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
777 vik_trw_layer_add_waypoint ( vtl, name, w );
778 waypoint_convert (NULL, w, &vtl->coord_mode);
780 // Consider if redraw necessary for the new item
781 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
782 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
785 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
790 t = vik_track_unmarshall(fi->data, fi->len);
791 // When copying - we'll create a new name based on the original
792 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
793 vik_trw_layer_add_track ( vtl, name, t );
794 track_convert (name, t, &vtl->coord_mode);
796 // Consider if redraw necessary for the new item
797 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
798 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
804 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
811 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
815 case PARAM_TV: vtl->tracks_visible = data.b; break;
816 case PARAM_WV: vtl->waypoints_visible = data.b; break;
817 case PARAM_DM: vtl->drawmode = data.u; break;
818 case PARAM_DP: vtl->drawpoints = data.b; break;
820 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
821 vtl->drawpoints_size = data.u;
823 case PARAM_DE: vtl->drawelevation = data.b; break;
824 case PARAM_DS: vtl->drawstops = data.b; break;
825 case PARAM_DL: vtl->drawlines = data.b; break;
826 case PARAM_DD: vtl->drawdirections = data.b; break;
828 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
829 vtl->drawdirections_size = data.u;
831 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
832 vtl->stop_length = data.u;
834 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
835 vtl->elevation_factor = data.u;
837 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
839 vtl->line_thickness = data.u;
840 trw_layer_new_track_gcs ( vtl, vp );
843 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
845 vtl->bg_line_thickness = data.u;
846 trw_layer_new_track_gcs ( vtl, vp );
849 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
850 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
851 case PARAM_DLA: vtl->drawlabels = data.b; break;
852 case PARAM_DI: vtl->drawimages = data.b; break;
853 case PARAM_IS: if ( data.u != vtl->image_size )
855 vtl->image_size = data.u;
856 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
857 g_queue_free ( vtl->image_cache );
858 vtl->image_cache = g_queue_new ();
861 case PARAM_IA: vtl->image_alpha = data.u; break;
862 case PARAM_ICS: vtl->image_cache_size = data.u;
863 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
864 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
866 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
867 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
868 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
869 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
870 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
871 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
872 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
873 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
878 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
880 VikLayerParamData rv;
883 case PARAM_TV: rv.b = vtl->tracks_visible; break;
884 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
885 case PARAM_DM: rv.u = vtl->drawmode; break;
886 case PARAM_DP: rv.b = vtl->drawpoints; break;
887 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
888 case PARAM_DE: rv.b = vtl->drawelevation; break;
889 case PARAM_EF: rv.u = vtl->elevation_factor; break;
890 case PARAM_DS: rv.b = vtl->drawstops; break;
891 case PARAM_SL: rv.u = vtl->stop_length; break;
892 case PARAM_DL: rv.b = vtl->drawlines; break;
893 case PARAM_DD: rv.b = vtl->drawdirections; break;
894 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
895 case PARAM_LT: rv.u = vtl->line_thickness; break;
896 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
897 case PARAM_DLA: rv.b = vtl->drawlabels; break;
898 case PARAM_DI: rv.b = vtl->drawimages; break;
899 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
900 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
901 case PARAM_IS: rv.u = vtl->image_size; break;
902 case PARAM_IA: rv.u = vtl->image_alpha; break;
903 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
904 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
905 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
906 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
907 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
908 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
909 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
910 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
911 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
916 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
927 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
928 a_gpx_write_file(vtl, f, NULL);
929 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
932 g_file_get_contents(tmpname, &dd, &dl, NULL);
933 *len = sizeof(pl) + pl + dl;
934 *data = g_malloc(*len);
935 memcpy(*data, &pl, sizeof(pl));
936 memcpy(*data + sizeof(pl), pd, pl);
937 memcpy(*data + sizeof(pl) + pl, dd, dl);
946 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
948 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
954 memcpy(&pl, data, sizeof(pl));
956 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
959 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
960 g_critical("couldn't open temp file");
963 fwrite(data, len - pl - sizeof(pl), 1, f);
965 a_gpx_read_file(rv, f);
973 // Keep interesting hash function at least visible
975 static guint strcase_hash(gconstpointer v)
977 // 31 bit hash function
980 gchar s[128]; // malloc is too slow for reading big files
983 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
984 p[i] = toupper(t[i]);
990 for (p += 1; *p != '\0'; p++)
991 h = (h << 5) - h + *p;
998 static VikTrwLayer* trw_layer_new ( gint drawmode )
1000 if (trw_layer_params[PARAM_DM].widget_data == NULL)
1001 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
1002 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
1003 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
1005 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1006 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1008 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1009 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1011 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1012 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1013 // and with normal PC processing capabilities - it has negligibile performance impact
1014 // This also minimized the amount of rework - as the management of the hash tables already exists.
1016 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1017 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1018 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1020 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1021 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1022 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1023 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1025 /* TODO: constants at top */
1026 rv->waypoints_visible = rv->tracks_visible = TRUE;
1027 rv->drawmode = drawmode;
1028 rv->drawpoints = TRUE;
1029 rv->drawpoints_size = MIN_POINT_SIZE;
1030 rv->drawdirections_size = 5;
1031 rv->drawstops = FALSE;
1032 rv->drawelevation = FALSE;
1033 rv->elevation_factor = 30;
1034 rv->stop_length = 60;
1035 rv->drawlines = TRUE;
1036 rv->wplabellayout = NULL;
1037 rv->wp_right_click_menu = NULL;
1038 rv->track_right_click_menu = NULL;
1039 rv->waypoint_gc = NULL;
1040 rv->waypoint_text_gc = NULL;
1041 rv->waypoint_bg_gc = NULL;
1042 rv->track_gc = NULL;
1043 rv->track_draw_speed_factor = 30.0;
1044 rv->line_thickness = 1;
1045 rv->bg_line_thickness = 0;
1046 rv->current_wp = NULL;
1047 rv->current_wp_id = NULL;
1048 rv->current_track = NULL;
1049 rv->current_tpl = NULL;
1050 rv->current_tp_track = NULL;
1051 rv->current_tp_id = NULL;
1052 rv->moving_tp = FALSE;
1053 rv->moving_wp = FALSE;
1055 rv->draw_sync_done = TRUE;
1056 rv->draw_sync_do = TRUE;
1058 rv->route_finder_started = FALSE;
1059 rv->route_finder_check_added_track = FALSE;
1060 rv->route_finder_current_track = NULL;
1061 rv->route_finder_append = FALSE;
1063 rv->waypoint_rightclick = FALSE;
1065 rv->image_cache = g_queue_new();
1066 rv->image_size = 64;
1067 rv->image_alpha = 255;
1068 rv->image_cache_size = 300;
1069 rv->drawimages = TRUE;
1070 rv->drawlabels = TRUE;
1075 static void trw_layer_free ( VikTrwLayer *trwlayer )
1077 g_hash_table_destroy(trwlayer->waypoints);
1078 g_hash_table_destroy(trwlayer->tracks);
1080 /* ODC: replace with GArray */
1081 trw_layer_free_track_gcs ( trwlayer );
1083 if ( trwlayer->wp_right_click_menu )
1084 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1086 if ( trwlayer->track_right_click_menu )
1087 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1089 if ( trwlayer->wplabellayout != NULL)
1090 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1092 if ( trwlayer->waypoint_gc != NULL )
1093 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1095 if ( trwlayer->waypoint_text_gc != NULL )
1096 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1098 if ( trwlayer->waypoint_bg_gc != NULL )
1099 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1101 if ( trwlayer->tpwin != NULL )
1102 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1104 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1105 g_queue_free ( trwlayer->image_cache );
1108 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1112 dp->xmpp = vik_viewport_get_xmpp ( vp );
1113 dp->ympp = vik_viewport_get_ympp ( vp );
1114 dp->width = vik_viewport_get_width ( vp );
1115 dp->height = vik_viewport_get_height ( vp );
1116 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1117 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1119 dp->center = vik_viewport_get_center ( vp );
1120 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1121 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1126 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1127 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1128 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1130 dp->ce1 = dp->center->east_west-w2;
1131 dp->ce2 = dp->center->east_west+w2;
1132 dp->cn1 = dp->center->north_south-h2;
1133 dp->cn2 = dp->center->north_south+h2;
1134 } else if ( dp->lat_lon ) {
1135 VikCoord upperleft, bottomright;
1136 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1137 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1138 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1139 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1140 dp->ce1 = upperleft.east_west;
1141 dp->ce2 = bottomright.east_west;
1142 dp->cn1 = bottomright.north_south;
1143 dp->cn2 = upperleft.north_south;
1146 dp->track_gc_iter = 0;
1150 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1151 * Here a simple traffic like light colour system is used:
1152 * . slow points are red
1153 * . average is yellow
1154 * . fast points are green
1156 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1159 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1160 if ( average_speed > 0 ) {
1161 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1162 if ( rv < low_speed )
1163 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1164 else if ( rv > high_speed )
1165 return VIK_TRW_LAYER_TRACK_GC_FAST;
1167 return VIK_TRW_LAYER_TRACK_GC_AVER;
1170 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1173 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1175 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1176 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1177 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1178 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1181 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1183 /* TODO: this function is a mess, get rid of any redundancy */
1184 GList *list = track->trackpoints;
1186 gboolean useoldvals = TRUE;
1188 gboolean drawpoints;
1190 gboolean drawelevation;
1191 gdouble min_alt, max_alt, alt_diff = 0;
1193 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1194 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1197 if ( dp->vtl->drawelevation )
1199 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1200 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1201 alt_diff = max_alt - min_alt;
1204 if ( ! track->visible )
1207 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1208 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1209 trw_layer_draw_track ( name, track, dp, TRUE );
1211 if ( draw_track_outline )
1212 drawpoints = drawstops = FALSE;
1214 drawpoints = dp->vtl->drawpoints;
1215 drawstops = dp->vtl->drawstops;
1218 gboolean drawing_highlight = FALSE;
1219 /* Current track - used for creation */
1220 if ( track == dp->vtl->current_track )
1221 main_gc = dp->vtl->current_track_gc;
1223 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1224 /* Draw all tracks of the layer in special colour */
1225 /* if track is member of selected layer or is the current selected track
1226 then draw in the highlight colour.
1227 NB this supercedes the drawmode */
1228 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1229 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1230 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1231 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1232 drawing_highlight = TRUE;
1235 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1236 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1238 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1242 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1243 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1245 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1250 int x, y, oldx, oldy;
1251 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1253 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1255 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1257 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1259 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1260 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1266 gdouble average_speed = 0.0;
1267 gdouble low_speed = 0.0;
1268 gdouble high_speed = 0.0;
1269 // If necessary calculate these values - which is done only once per track redraw
1270 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1271 // the percentage factor away from the average speed determines transistions between the levels
1272 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1273 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1274 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1277 while ((list = g_list_next(list)))
1279 tp = VIK_TRACKPOINT(list->data);
1280 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1282 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1283 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1284 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1285 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1286 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1288 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1291 * If points are the same in display coordinates, don't draw.
1293 if ( useoldvals && x == oldx && y == oldy )
1295 // Still need to process points to ensure 'stops' are drawn if required
1296 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1297 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1298 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
1303 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1304 if ( drawpoints || dp->vtl->drawlines ) {
1305 // setup main_gc for both point and line drawing
1306 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1307 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1308 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1312 if ( drawpoints && ! draw_track_outline )
1317 * The concept of drawing stops is that a trackpoint
1318 * that is if the next trackpoint has a timestamp far into
1319 * the future, we draw a circle of 6x trackpoint size,
1320 * instead of a rectangle of 2x trackpoint size.
1321 * This is drawn first so the trackpoint will be drawn on top
1324 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1325 /* Stop point. Draw 6x circle. */
1326 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
1328 /* Regular point - draw 2x square. */
1329 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1332 /* Final point - draw 4x circle. */
1333 vik_viewport_draw_arc ( dp->vp, main_gc, TRUE, x-(2*tp_size), y-(2*tp_size), 4*tp_size, 4*tp_size, 0, 360*64 );
1336 if ((!tp->newsegment) && (dp->vtl->drawlines))
1339 /* UTM only: zone check */
1340 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1341 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1344 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1346 if ( draw_track_outline ) {
1347 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1351 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1353 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1355 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1360 tmp[1].y = oldy-FIXALTITUDE(list->data);
1362 tmp[2].y = y-FIXALTITUDE(list->next->data);
1367 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1368 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1370 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1371 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1373 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1378 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1379 // Draw an arrow at the mid point to show the direction of the track
1380 // Code is a rework from vikwindow::draw_ruler()
1381 gint midx = (oldx + x) / 2;
1382 gint midy = (oldy + y) / 2;
1384 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1385 // Avoid divide by zero and ensure at least 1 pixel big
1387 gdouble dx = (oldx - midx) / len;
1388 gdouble dy = (oldy - midy) / len;
1389 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1390 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1400 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1402 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1403 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1405 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1407 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1408 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1409 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1413 * If points are the same in display coordinates, don't draw.
1415 if ( x != oldx || y != oldy )
1417 if ( draw_track_outline )
1418 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1420 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1426 * If points are the same in display coordinates, don't draw.
1428 if ( x != oldx && y != oldy )
1430 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1431 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1439 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1440 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
1441 dp->track_gc_iter = 0;
1444 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1445 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1447 trw_layer_draw_track ( name, track, dp, FALSE );
1450 static void cached_pixbuf_free ( CachedPixbuf *cp )
1452 g_object_unref ( G_OBJECT(cp->pixbuf) );
1453 g_free ( cp->image );
1456 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1458 return strcmp ( cp->image, name );
1461 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1464 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1465 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1466 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1469 GdkPixbuf *sym = NULL;
1470 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1472 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1474 if ( wp->image && dp->vtl->drawimages )
1476 GdkPixbuf *pixbuf = NULL;
1479 if ( dp->vtl->image_alpha == 0)
1482 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1484 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1487 gchar *image = wp->image;
1488 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1489 if ( ! regularthumb )
1491 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1492 image = "\x12\x00"; /* this shouldn't occur naturally. */
1496 CachedPixbuf *cp = NULL;
1497 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1498 if ( dp->vtl->image_size == 128 )
1499 cp->pixbuf = regularthumb;
1502 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1503 g_assert ( cp->pixbuf );
1504 g_object_unref ( G_OBJECT(regularthumb) );
1506 cp->image = g_strdup ( image );
1508 /* needed so 'click picture' tool knows how big the pic is; we don't
1509 * store it in cp because they may have been freed already. */
1510 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1511 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1513 g_queue_push_head ( dp->vtl->image_cache, cp );
1514 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1515 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1517 pixbuf = cp->pixbuf;
1521 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1527 w = gdk_pixbuf_get_width ( pixbuf );
1528 h = gdk_pixbuf_get_height ( pixbuf );
1530 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1532 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1533 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1534 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1535 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1536 // Highlighted - so draw a little border around the chosen one
1537 // single line seems a little weak so draw 2 of them
1538 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1539 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1540 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1541 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1544 if ( dp->vtl->image_alpha == 255 )
1545 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1547 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1549 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1553 /* DRAW ACTUAL DOT */
1554 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1555 vik_viewport_draw_pixbuf ( dp->vp, sym, 0, 0, x - gdk_pixbuf_get_width(sym)/2, y - gdk_pixbuf_get_height(sym)/2, -1, -1 );
1557 else if ( wp == dp->vtl->current_wp ) {
1558 switch ( dp->vtl->wp_symbol ) {
1559 case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
1560 case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
1561 case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size, y - dp->vtl->wp_size, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
1562 case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y - dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y + dp->vtl->wp_size*2 );
1563 vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y + dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y - dp->vtl->wp_size*2 );
1567 switch ( dp->vtl->wp_symbol ) {
1568 case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break;
1569 case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break;
1570 case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x-dp->vtl->wp_size/2, y-dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
1571 case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y-dp->vtl->wp_size, x+dp->vtl->wp_size, y+dp->vtl->wp_size );
1572 vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y+dp->vtl->wp_size, x+dp->vtl->wp_size, y-dp->vtl->wp_size ); break;
1576 if ( dp->vtl->drawlabels )
1578 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1579 gint label_x, label_y;
1581 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1583 // Could this stored in the waypoint rather than recreating each pass?
1584 gchar *fsize = NULL;
1585 switch (dp->vtl->wp_font_size) {
1586 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1587 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1588 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1589 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1590 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1591 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1592 default: fsize = g_strdup ( "medium" ); break;
1595 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1597 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1598 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1600 // Fallback if parse failure
1601 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1603 g_free ( wp_label_markup );
1606 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1607 label_x = x - width/2;
1609 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1611 label_y = y - dp->vtl->wp_size - height - 2;
1613 /* if highlight mode on, then draw background text in highlight colour */
1614 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1615 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1616 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1617 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1618 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1620 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1623 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1625 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1630 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1632 static struct DrawingParams dp;
1633 g_assert ( l != NULL );
1635 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1637 if ( l->tracks_visible )
1638 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1640 if (l->waypoints_visible)
1641 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1644 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1647 if ( vtl->track_bg_gc )
1649 g_object_unref ( vtl->track_bg_gc );
1650 vtl->track_bg_gc = NULL;
1652 if ( vtl->current_track_gc )
1654 g_object_unref ( vtl->current_track_gc );
1655 vtl->current_track_gc = NULL;
1657 if ( vtl->current_track_newpoint_gc )
1659 g_object_unref ( vtl->current_track_newpoint_gc );
1660 vtl->current_track_newpoint_gc = NULL;
1663 if ( ! vtl->track_gc )
1665 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1666 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1667 g_array_free ( vtl->track_gc, TRUE );
1668 vtl->track_gc = NULL;
1671 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1673 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1674 gint width = vtl->line_thickness;
1676 if ( vtl->track_gc )
1677 trw_layer_free_track_gcs ( vtl );
1679 if ( vtl->track_bg_gc )
1680 g_object_unref ( vtl->track_bg_gc );
1681 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1683 // Ensure new track drawing heeds line thickness setting
1684 // however always have a minium of 2, as 1 pixel is really narrow
1685 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1687 if ( vtl->current_track_gc )
1688 g_object_unref ( vtl->current_track_gc );
1689 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1690 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1692 // 'newpoint' gc is exactly the same as the current track gc
1693 if ( vtl->current_track_newpoint_gc )
1694 g_object_unref ( vtl->current_track_newpoint_gc );
1695 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1696 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1698 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1700 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1702 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1703 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1704 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1705 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1706 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1707 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1708 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1709 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1710 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1711 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1713 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1715 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1717 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1718 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1719 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1721 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1724 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1726 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1727 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1729 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1730 /* early exit, as the rest is GUI related */
1734 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1735 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1737 trw_layer_new_track_gcs ( rv, vp );
1739 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1740 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1741 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1742 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1744 rv->has_verified_thumbnails = FALSE;
1745 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1747 rv->wp_draw_symbols = TRUE;
1748 rv->wp_font_size = FS_MEDIUM;
1750 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1752 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1757 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1759 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1761 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1762 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1764 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1767 *new_iter = *((GtkTreeIter *) pass_along[1]);
1768 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1770 if ( ! track->visible )
1771 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1774 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1776 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1777 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1778 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1780 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), NULL, TRUE, TRUE );
1783 *new_iter = *((GtkTreeIter *) pass_along[1]);
1784 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1786 if ( ! wp->visible )
1787 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1790 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1792 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1793 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1795 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1799 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1801 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1802 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1804 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1808 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1811 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1813 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1814 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1815 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1817 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1820 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
1821 trw_layer_add_sublayer_waypoints ( vtl, vt , layer_iter );
1823 pass_along[0] = &(vtl->waypoints_iter);
1824 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1826 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1828 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1832 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1836 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1837 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1838 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1840 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1842 return (t->visible ^= 1);
1846 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1848 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1850 return (t->visible ^= 1);
1859 * Return a property about tracks for this layer
1861 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1863 return vtl->line_thickness;
1866 // Structure to hold multiple track information for a layer
1875 * Build up layer multiple track information via updating the tooltip_tracks structure
1877 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1879 tt->length = tt->length + vik_track_get_length (tr);
1881 // Ensure times are available
1882 if ( tr->trackpoints &&
1883 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1884 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1887 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1888 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1890 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1891 // Hence initialize to the first 'proper' value
1892 if ( tt->start_time == 0 )
1893 tt->start_time = t1;
1894 if ( tt->end_time == 0 )
1897 // Update find the earliest / last times
1898 if ( t1 < tt->start_time )
1899 tt->start_time = t1;
1900 if ( t2 > tt->end_time )
1903 // Keep track of total time
1904 // there maybe gaps within a track (eg segments)
1905 // but this should be generally good enough for a simple indicator
1906 tt->duration = tt->duration + (int)(t2-t1);
1911 * Generate tooltip text for the layer.
1912 * This is relatively complicated as it considers information for
1913 * no tracks, a single track or multiple tracks
1914 * (which may or may not have timing information)
1916 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1927 static gchar tmp_buf[128];
1930 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1932 // Safety check - I think these should always be valid
1933 if ( vtl->tracks && vtl->waypoints ) {
1934 tooltip_tracks tt = { 0.0, 0, 0 };
1935 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1937 GDate* gdate_start = g_date_new ();
1938 g_date_set_time_t (gdate_start, tt.start_time);
1940 GDate* gdate_end = g_date_new ();
1941 g_date_set_time_t (gdate_end, tt.end_time);
1943 if ( g_date_compare (gdate_start, gdate_end) ) {
1944 // Dates differ so print range on separate line
1945 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1946 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1947 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1950 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1951 if ( tt.start_time != 0 )
1952 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1956 if ( tt.length > 0.0 ) {
1957 gdouble len_in_units;
1959 // Setup info dependent on distance units
1960 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1961 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1962 len_in_units = VIK_METERS_TO_MILES(tt.length);
1965 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1966 len_in_units = tt.length/1000.0;
1969 // Timing information if available
1971 if ( tt.duration > 0 ) {
1972 g_snprintf (tbuf1, sizeof(tbuf1),
1973 _(" in %d:%02d hrs:mins"),
1974 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1976 g_snprintf (tbuf2, sizeof(tbuf2),
1977 _("\n%sTotal Length %.1f %s%s"),
1978 tbuf3, len_in_units, tbuf4, tbuf1);
1981 // Put together all the elements to form compact tooltip text
1982 g_snprintf (tmp_buf, sizeof(tmp_buf),
1983 _("Tracks: %d - Waypoints: %d%s"),
1984 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1986 g_date_free (gdate_start);
1987 g_date_free (gdate_end);
1994 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1998 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2000 // Very simple tooltip - may expand detail in the future...
2001 static gchar tmp_buf[32];
2002 g_snprintf (tmp_buf, sizeof(tmp_buf),
2004 g_hash_table_size (l->tracks));
2008 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2010 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
2012 // Could be a better way of handling strings - but this works...
2013 gchar time_buf1[20];
2014 gchar time_buf2[20];
2015 time_buf1[0] = '\0';
2016 time_buf2[0] = '\0';
2017 static gchar tmp_buf[100];
2018 // Compact info: Short date eg (11/20/99), duration and length
2019 // Hopefully these are the things that are most useful and so promoted into the tooltip
2020 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2021 // %x The preferred date representation for the current locale without the time.
2022 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2023 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2024 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2026 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2029 // Get length and consider the appropriate distance units
2030 gdouble tr_len = vik_track_get_length(tr);
2031 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2032 switch (dist_units) {
2033 case VIK_UNITS_DISTANCE_KILOMETRES:
2034 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2036 case VIK_UNITS_DISTANCE_MILES:
2037 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2046 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2048 // Very simple tooltip - may expand detail in the future...
2049 static gchar tmp_buf[32];
2050 g_snprintf (tmp_buf, sizeof(tmp_buf),
2052 g_hash_table_size (l->waypoints));
2056 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2058 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2059 // NB It's OK to return NULL
2070 * Function to show basic track point information on the statusbar
2072 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2075 switch (a_vik_get_units_height ()) {
2076 case VIK_UNITS_HEIGHT_FEET:
2077 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2080 //VIK_UNITS_HEIGHT_METRES:
2081 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2086 if ( trkpt->has_timestamp ) {
2087 // Compact date time format
2088 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2092 // Position is put later on, as this bit may not be seen if the display is not big enough,
2093 // one can easily use the current pointer position to see this if needed
2094 gchar *lat = NULL, *lon = NULL;
2095 static struct LatLon ll;
2096 vik_coord_to_latlon (&(trkpt->coord), &ll);
2097 a_coords_latlon_to_string ( &ll, &lat, &lon );
2100 // Again is put later on, as this bit may not be seen if the display is not big enough
2101 // trackname can be seen from the treeview (when enabled)
2102 // Also name could be very long to not leave room for anything else
2105 if ( vtl->current_tp_track ) {
2106 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2109 // Combine parts to make overall message
2110 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2111 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2118 * Function to show basic waypoint information on the statusbar
2120 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2123 switch (a_vik_get_units_height ()) {
2124 case VIK_UNITS_HEIGHT_FEET:
2125 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2128 //VIK_UNITS_HEIGHT_METRES:
2129 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2133 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2134 // one can easily use the current pointer position to see this if needed
2135 gchar *lat = NULL, *lon = NULL;
2136 static struct LatLon ll;
2137 vik_coord_to_latlon (&(wpt->coord), &ll);
2138 a_coords_latlon_to_string ( &ll, &lat, &lon );
2140 // Combine parts to make overall message
2143 // Add comment if available
2144 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2146 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2147 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2154 * General layer selection function, find out which bit is selected and take appropriate action
2156 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2159 l->current_wp = NULL;
2160 l->current_wp_id = NULL;
2161 trw_layer_cancel_current_tp ( l, FALSE );
2164 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2168 case VIK_TREEVIEW_TYPE_LAYER:
2170 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2171 /* Mark for redraw */
2176 case VIK_TREEVIEW_TYPE_SUBLAYER:
2180 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2182 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2183 /* Mark for redraw */
2187 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2189 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2190 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2191 /* Mark for redraw */
2195 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2197 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2198 /* Mark for redraw */
2202 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2204 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2206 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2207 // Show some waypoint info
2208 set_statusbar_msg_info_wpt ( l, wpt );
2209 /* Mark for redraw */
2216 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2225 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2230 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2235 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2237 return l->waypoints;
2241 * ATM use a case sensitive find
2242 * Finds the first one
2244 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2246 if ( wp && wp->name )
2247 if ( ! strcmp ( wp->name, name ) )
2253 * Get waypoint by name - not guaranteed to be unique
2254 * Finds the first one
2256 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2258 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2262 * ATM use a case sensitive find
2263 * Finds the first one
2265 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2267 if ( trk && trk->name )
2268 if ( ! strcmp ( trk->name, name ) )
2274 * Get track by name - not guaranteed to be unique
2275 * Finds the first one
2277 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2279 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2282 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2284 static VikCoord fixme;
2285 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2286 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2287 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2288 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2289 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2290 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2291 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2292 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2293 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2296 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2298 GList *tr = trk->trackpoints;
2299 static VikCoord fixme;
2303 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2304 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2305 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2306 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2307 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2308 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2309 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2310 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2311 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2316 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2318 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2319 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2321 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2322 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2323 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2324 maxmin[0].lat = wpt_maxmin[0].lat;
2327 maxmin[0].lat = trk_maxmin[0].lat;
2329 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2330 maxmin[0].lon = wpt_maxmin[0].lon;
2333 maxmin[0].lon = trk_maxmin[0].lon;
2335 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2336 maxmin[1].lat = wpt_maxmin[1].lat;
2339 maxmin[1].lat = trk_maxmin[1].lat;
2341 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2342 maxmin[1].lon = wpt_maxmin[1].lon;
2345 maxmin[1].lon = trk_maxmin[1].lon;
2349 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2351 /* 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... */
2352 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2353 trw_layer_find_maxmin (vtl, maxmin);
2354 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2358 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2359 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2364 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2367 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2368 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2370 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2373 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2375 /* First set the center [in case previously viewing from elsewhere] */
2376 /* Then loop through zoom levels until provided positions are in view */
2377 /* This method is not particularly fast - but should work well enough */
2378 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2380 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2381 vik_viewport_set_center_coord ( vvp, &coord );
2383 /* Convert into definite 'smallest' and 'largest' positions */
2384 struct LatLon minmin;
2385 if ( maxmin[0].lat < maxmin[1].lat )
2386 minmin.lat = maxmin[0].lat;
2388 minmin.lat = maxmin[1].lat;
2390 struct LatLon maxmax;
2391 if ( maxmin[0].lon > maxmin[1].lon )
2392 maxmax.lon = maxmin[0].lon;
2394 maxmax.lon = maxmin[1].lon;
2396 /* Never zoom in too far - generally not that useful, as too close ! */
2397 /* Always recalculate the 'best' zoom level */
2399 vik_viewport_set_zoom ( vvp, zoom );
2401 gdouble min_lat, max_lat, min_lon, max_lon;
2402 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2403 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2404 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2405 /* NB I think the logic used in this test to determine if the bounds is within view
2406 fails if track goes across 180 degrees longitude.
2407 Hopefully that situation is not too common...
2408 Mind you viking doesn't really do edge locations to well anyway */
2409 if ( min_lat < minmin.lat &&
2410 max_lat > minmin.lat &&
2411 min_lon < maxmax.lon &&
2412 max_lon > maxmax.lon )
2413 /* Found within zoom level */
2418 vik_viewport_set_zoom ( vvp, zoom );
2422 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2424 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2425 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2426 trw_layer_find_maxmin (vtl, maxmin);
2427 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2430 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2435 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2437 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])) ) ) {
2438 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2441 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2444 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2446 GtkWidget *file_selector;
2448 gboolean failed = FALSE;
2449 file_selector = gtk_file_chooser_dialog_new (title,
2451 GTK_FILE_CHOOSER_ACTION_SAVE,
2452 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2453 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2455 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2457 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2459 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2460 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2462 gtk_widget_hide ( file_selector );
2463 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2468 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2470 gtk_widget_hide ( file_selector );
2471 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2476 gtk_widget_destroy ( file_selector );
2478 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2481 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2483 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2486 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2488 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2491 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2493 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2494 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2495 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2496 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2498 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2500 g_free ( auto_save_name );
2503 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2505 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2506 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2507 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2508 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2510 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2512 g_free ( auto_save_name );
2516 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2519 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2521 gchar *name_used = NULL;
2524 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2525 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2527 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2531 gchar *quoted_file = g_shell_quote ( name_used );
2532 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2533 g_free ( quoted_file );
2534 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2536 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2537 g_error_free ( err );
2541 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2542 //g_remove ( name_used );
2543 // Perhaps should be deleted when the program ends?
2544 // For now leave it to the user to delete it / use system temp cleanup methods.
2545 g_free ( name_used );
2549 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2551 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2554 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2556 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2559 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2561 gpointer layer_and_vlp[2];
2562 layer_and_vlp[0] = pass_along[0];
2563 layer_and_vlp[1] = pass_along[1];
2564 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2566 if ( !trk || !trk->name )
2569 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2570 gchar *auto_save_name = g_strdup ( trk->name );
2571 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2572 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2574 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2576 g_free ( auto_save_name );
2580 VikWaypoint *wp; // input
2581 gpointer uuid; // output
2584 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2586 wpu_udata *user_data = udata;
2587 if ( wp == user_data->wp ) {
2588 user_data->uuid = id;
2594 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2596 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2597 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2598 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2600 GTK_RESPONSE_REJECT,
2602 GTK_RESPONSE_ACCEPT,
2605 GtkWidget *label, *entry;
2606 label = gtk_label_new(_("Waypoint Name:"));
2607 entry = gtk_entry_new();
2609 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2610 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2611 gtk_widget_show_all ( label );
2612 gtk_widget_show_all ( entry );
2614 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2616 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2618 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2619 // Find *first* wp with the given name
2620 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2623 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2626 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2627 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2629 // Find and select on the side panel
2634 // Hmmm, want key of it
2635 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2637 if ( wpf && udata.uuid ) {
2638 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2639 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2648 gtk_widget_destroy ( dia );
2651 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2653 gchar *default_name = highest_wp_number_get(vtl);
2654 VikWaypoint *wp = vik_waypoint_new();
2655 gchar *returned_name;
2657 wp->coord = *def_coord;
2659 // Attempt to auto set height if DEM data is available
2660 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2661 if ( elev != VIK_DEM_INVALID_ELEVATION )
2662 wp->altitude = (gdouble)elev;
2664 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2666 if ( returned_name )
2669 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2670 g_free (default_name);
2671 g_free (returned_name);
2674 g_free (default_name);
2675 vik_waypoint_free(wp);
2679 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2682 struct LatLon one_ll, two_ll;
2683 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2685 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2686 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2687 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2688 VikViewport *vvp = vik_window_viewport(vw);
2689 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2690 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2691 vik_coord_to_latlon(&one, &one_ll);
2692 vik_coord_to_latlon(&two, &two_ll);
2693 if (one_ll.lat > two_ll.lat) {
2694 maxmin[0].lat = one_ll.lat;
2695 maxmin[1].lat = two_ll.lat;
2698 maxmin[0].lat = two_ll.lat;
2699 maxmin[1].lat = one_ll.lat;
2701 if (one_ll.lon > two_ll.lon) {
2702 maxmin[0].lon = one_ll.lon;
2703 maxmin[1].lon = two_ll.lon;
2706 maxmin[0].lon = two_ll.lon;
2707 maxmin[1].lon = one_ll.lon;
2709 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2712 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2714 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2715 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2716 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2718 trw_layer_find_maxmin (vtl, maxmin);
2719 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2722 #ifdef VIK_CONFIG_GEOTAG
2723 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2725 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2727 // Update directly - not changing the mtime
2728 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2731 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2733 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2736 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2740 * Use code in separate file for this feature as reasonably complex
2742 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2744 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2745 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2746 // Unset so can be reverified later if necessary
2747 vtl->has_verified_thumbnails = FALSE;
2749 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2755 static void trw_layer_geotagging ( gpointer lav[2] )
2757 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2758 // Unset so can be reverified later if necessary
2759 vtl->has_verified_thumbnails = FALSE;
2761 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2768 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2771 * Acquire into this TRW Layer straight from GPS Device
2773 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2775 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2776 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2777 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2778 VikViewport *vvp = vik_window_viewport(vw);
2780 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2781 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2784 #ifdef VIK_CONFIG_GOOGLE
2786 * Acquire into this TRW Layer from Google Directions
2788 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2790 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2791 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2792 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2793 VikViewport *vvp = vik_window_viewport(vw);
2795 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2799 #ifdef VIK_CONFIG_OPENSTREETMAP
2801 * Acquire into this TRW Layer from OSM
2803 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2805 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2806 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2807 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2808 VikViewport *vvp = vik_window_viewport(vw);
2810 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2814 #ifdef VIK_CONFIG_GEOCACHES
2816 * Acquire into this TRW Layer from Geocaching.com
2818 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2820 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2821 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2822 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2823 VikViewport *vvp = vik_window_viewport(vw);
2825 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2829 #ifdef VIK_CONFIG_GEOTAG
2831 * Acquire into this TRW Layer from images
2833 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2835 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2836 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2837 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2838 VikViewport *vvp = vik_window_viewport(vw);
2840 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2841 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2843 // Reverify thumbnails as they may have changed
2844 vtl->has_verified_thumbnails = FALSE;
2845 trw_layer_verify_thumbnails ( vtl, NULL );
2849 static void trw_layer_gps_upload ( gpointer lav[2] )
2851 gpointer pass_along[6];
2852 pass_along[0] = lav[0];
2853 pass_along[1] = lav[1];
2854 pass_along[2] = NULL; // No track - operate on the layer
2855 pass_along[3] = NULL;
2856 pass_along[4] = NULL;
2857 pass_along[5] = NULL;
2859 trw_layer_gps_upload_any ( pass_along );
2863 * If pass_along[3] is defined that this will upload just that track
2865 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
2867 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2868 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
2870 // May not actually get a track here as pass_along[3] can be null
2871 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2873 gboolean on_track = track ? TRUE : FALSE;
2875 if (on_track && !track->visible) {
2876 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
2880 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
2881 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2882 GTK_DIALOG_DESTROY_WITH_PARENT,
2884 GTK_RESPONSE_ACCEPT,
2886 GTK_RESPONSE_REJECT,
2889 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2890 GtkWidget *response_w = NULL;
2891 #if GTK_CHECK_VERSION (2, 20, 0)
2892 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2896 gtk_widget_grab_focus ( response_w );
2898 gpointer dgs = datasource_gps_setup ( dialog, on_track );
2900 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2901 datasource_gps_clean_up ( dgs );
2902 gtk_widget_destroy ( dialog );
2906 // Get info from reused datasource dialog widgets
2907 gchar* protocol = datasource_gps_get_protocol ( dgs );
2908 gchar* port = datasource_gps_get_descriptor ( dgs );
2909 // NB don't free the above strings as they're references to values held elsewhere
2910 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
2911 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
2912 gboolean turn_off = datasource_gps_get_off ( dgs );
2914 gtk_widget_destroy ( dialog );
2916 // When called from the viewport - work the corresponding layerspanel:
2918 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
2921 // Apply settings to transfer to the GPS device
2928 vik_layers_panel_get_viewport (vlp),
2936 * Acquire into this TRW Layer from any GPS Babel supported file
2938 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
2940 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2941 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2942 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2943 VikViewport *vvp = vik_window_viewport(vw);
2945 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
2948 static void trw_layer_new_wp ( gpointer lav[2] )
2950 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2951 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2952 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2953 instead return true if you want to update. */
2954 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 )
2955 vik_layers_panel_emit_update ( vlp );
2958 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2960 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2961 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2963 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2964 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2965 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2966 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2967 vik_layers_panel_emit_update ( vlp );
2971 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2973 /* NB do not care if wp is visible or not */
2974 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2977 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2979 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2980 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2982 /* Only 1 waypoint - jump straight to it */
2983 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2984 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2985 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2987 /* If at least 2 waypoints - find center and then zoom to fit */
2988 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2990 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2991 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2992 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2995 vik_layers_panel_emit_update ( vlp );
2998 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3000 static gpointer pass_along[2];
3002 GtkWidget *export_submenu;
3003 pass_along[0] = vtl;
3004 pass_along[1] = vlp;
3006 item = gtk_menu_item_new();
3007 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3008 gtk_widget_show ( item );
3010 /* Now with icons */
3011 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3012 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3013 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3014 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3015 gtk_widget_show ( item );
3017 GtkWidget *view_submenu = gtk_menu_new();
3018 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3019 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3020 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3021 gtk_widget_show ( item );
3022 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3024 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3026 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3027 gtk_widget_show ( item );
3029 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3031 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3032 gtk_widget_show ( item );
3034 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3035 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3037 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3038 gtk_widget_show ( item );
3040 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3042 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3043 gtk_widget_show ( item );
3045 export_submenu = gtk_menu_new ();
3046 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3047 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3048 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3049 gtk_widget_show ( item );
3050 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3052 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3054 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3055 gtk_widget_show ( item );
3057 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3058 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3059 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3060 gtk_widget_show ( item );
3062 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3063 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3064 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3065 gtk_widget_show ( item );
3067 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3069 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3070 gtk_widget_show ( item );
3072 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3073 item = gtk_menu_item_new_with_mnemonic ( external1 );
3074 g_free ( external1 );
3075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3076 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3077 gtk_widget_show ( item );
3079 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3080 item = gtk_menu_item_new_with_mnemonic ( external2 );
3081 g_free ( external2 );
3082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3083 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3084 gtk_widget_show ( item );
3086 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3087 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3089 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3090 gtk_widget_show ( item );
3092 #ifdef VIK_CONFIG_GEONAMES
3093 GtkWidget *wikipedia_submenu = gtk_menu_new();
3094 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3095 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3096 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3097 gtk_widget_show(item);
3098 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3100 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3101 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3102 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3103 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3104 gtk_widget_show ( item );
3106 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3107 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3109 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3110 gtk_widget_show ( item );
3113 #ifdef VIK_CONFIG_GEOTAG
3114 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3116 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3117 gtk_widget_show ( item );
3120 GtkWidget *acquire_submenu = gtk_menu_new ();
3121 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3123 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3124 gtk_widget_show ( item );
3125 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3127 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3128 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3129 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3130 gtk_widget_show ( item );
3132 #ifdef VIK_CONFIG_GOOGLE
3133 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3134 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3135 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3136 gtk_widget_show ( item );
3139 #ifdef VIK_CONFIG_OPENSTREETMAP
3140 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3141 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3142 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3143 gtk_widget_show ( item );
3146 #ifdef VIK_CONFIG_GEOCACHES
3147 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3148 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3149 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3150 gtk_widget_show ( item );
3153 #ifdef VIK_CONFIG_GEOTAG
3154 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3155 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3156 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3157 gtk_widget_show ( item );
3160 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3161 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3162 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3163 gtk_widget_show ( item );
3165 GtkWidget *upload_submenu = gtk_menu_new ();
3166 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3167 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3168 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3169 gtk_widget_show ( item );
3170 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3172 #ifdef VIK_CONFIG_OPENSTREETMAP
3173 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3174 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3176 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3177 gtk_widget_show ( item );
3180 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3183 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3184 gtk_widget_show ( item );
3186 GtkWidget *delete_submenu = gtk_menu_new ();
3187 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3188 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3189 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3190 gtk_widget_show ( item );
3191 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3193 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3194 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3196 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3197 gtk_widget_show ( item );
3199 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3200 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3201 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3202 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3203 gtk_widget_show ( item );
3205 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3206 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3208 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3209 gtk_widget_show ( item );
3211 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3212 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3213 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3214 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3215 gtk_widget_show ( item );
3217 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3218 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3220 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3221 gtk_widget_show ( item );
3224 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3225 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3227 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3228 gtk_widget_show ( item );
3232 // Fake Waypoint UUIDs vi simple increasing integer
3233 static guint wp_uuid = 0;
3235 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3239 vik_waypoint_set_name (wp, name);
3241 if ( VIK_LAYER(vtl)->realized )
3243 // Do we need to create the sublayer:
3244 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3245 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3248 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3250 // Visibility column always needed for waypoints
3251 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3252 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 );
3254 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 );
3256 // Actual setting of visibility dependent on the waypoint
3257 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3259 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3262 highest_wp_number_add_wp(vtl, name);
3263 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3267 // Fake Track UUIDs vi simple increasing integer
3268 static guint tr_uuid = 0;
3270 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3274 vik_track_set_name (t, name);
3276 if ( VIK_LAYER(vtl)->realized )
3278 // Do we need to create the sublayer:
3279 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3280 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3283 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3284 // Visibility column always needed for tracks
3285 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3286 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 );
3288 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 );
3290 // Actual setting of visibility dependent on the track
3291 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3293 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3296 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3300 /* to be called whenever a track has been deleted or may have been changed. */
3301 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3303 if (vtl->current_tp_track == trk )
3304 trw_layer_cancel_current_tp ( vtl, FALSE );
3307 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3310 gchar *newname = g_strdup(name);
3311 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
3312 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
3313 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3315 newname = new_newname;
3321 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3323 // No more uniqueness of name forced when loading from a file
3324 // This now makes this function a little redunant as we just flow the parameters through
3325 vik_trw_layer_add_waypoint ( vtl, name, wp );
3328 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3330 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3331 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3332 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3333 vik_track_free ( tr );
3334 vtl->route_finder_append = FALSE; /* this means we have added it */
3337 // No more uniqueness of name forced when loading from a file
3338 vik_trw_layer_add_track ( vtl, name, tr );
3340 if ( vtl->route_finder_check_added_track ) {
3341 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3342 vtl->route_finder_added_track = tr;
3347 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3349 *l = g_list_append(*l, id);
3353 * Move an item from one TRW layer to another TRW layer
3355 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3357 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3358 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3360 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3362 VikTrack *trk2 = vik_track_copy ( trk );
3363 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3364 vik_trw_layer_delete_track ( vtl_src, trk );
3367 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3368 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3370 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3372 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3373 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3374 trw_layer_delete_waypoint ( vtl_src, wp );
3378 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3380 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3381 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3383 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3384 GList *items = NULL;
3387 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3388 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3390 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3391 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3396 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3397 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3399 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3406 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3407 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3412 VikTrack *trk; // input
3413 gpointer uuid; // output
3416 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3418 trku_udata *user_data = udata;
3419 if ( trk == user_data->trk ) {
3420 user_data->uuid = id;
3426 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3428 gboolean was_visible = FALSE;
3430 if ( trk && trk->name ) {
3432 if ( trk == vtl->current_track ) {
3433 vtl->current_track = NULL;
3434 vtl->current_tp_track = NULL;
3435 vtl->current_tp_id = NULL;
3436 vtl->moving_tp = FALSE;
3439 was_visible = trk->visible;
3441 if ( trk == vtl->route_finder_current_track )
3442 vtl->route_finder_current_track = NULL;
3444 if ( trk == vtl->route_finder_added_track )
3445 vtl->route_finder_added_track = NULL;
3451 // Hmmm, want key of it
3452 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3454 if ( trkf && udata.uuid ) {
3455 /* could be current_tp, so we have to check */
3456 trw_layer_cancel_tps_of_track ( vtl, trk );
3458 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3461 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3462 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3463 g_hash_table_remove ( vtl->tracks, udata.uuid );
3465 // If last sublayer, then remove sublayer container
3466 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3467 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3475 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3477 gboolean was_visible = FALSE;
3479 if ( wp && wp->name ) {
3481 if ( wp == vtl->current_wp ) {
3482 vtl->current_wp = NULL;
3483 vtl->current_wp_id = NULL;
3484 vtl->moving_wp = FALSE;
3487 was_visible = wp->visible;
3493 // Hmmm, want key of it
3494 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3496 if ( wpf && udata.uuid ) {
3497 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3500 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3501 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3503 highest_wp_number_remove_wp(vtl, wp->name);
3504 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3506 // If last sublayer, then remove sublayer container
3507 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3508 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3518 // Only for temporary use by trw_layer_delete_waypoint_by_name
3519 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3521 wpu_udata *user_data = udata;
3522 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3523 user_data->uuid = id;
3530 * Delete a waypoint by the given name
3531 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3532 * as there be multiple waypoints with the same name
3534 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3537 // Fake a waypoint with the given name
3538 udata.wp = vik_waypoint_new ();
3539 vik_waypoint_set_name (udata.wp, name);
3540 // Currently only the name is used in this waypoint find function
3543 // Hmmm, want key of it
3544 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3546 vik_waypoint_free (udata.wp);
3548 if ( wpf && udata.uuid )
3549 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3555 VikTrack *trk; // input
3556 gpointer uuid; // output
3559 // Only for temporary use by trw_layer_delete_track_by_name
3560 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3562 tpu_udata *user_data = udata;
3563 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3564 user_data->uuid = id;
3571 * Delete a track by the given name
3572 * NOTE: ATM this will delete the first encountered Track with the specified name
3573 * as there be multiple track with the same name
3575 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3578 // Fake a track with the given name
3579 udata.trk = vik_track_new ();
3580 vik_track_set_name (udata.trk, name);
3581 // Currently only the name is used in this waypoint find function
3584 // Hmmm, want key of it
3585 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3587 vik_track_free (udata.trk);
3589 if ( trkf && udata.uuid )
3590 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3595 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3597 vik_treeview_item_delete (vt, it );
3600 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3603 vtl->current_track = NULL;
3604 vtl->route_finder_current_track = NULL;
3605 vtl->route_finder_added_track = NULL;
3606 if (vtl->current_tp_track)
3607 trw_layer_cancel_current_tp(vtl, FALSE);
3609 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3610 g_hash_table_remove_all(vtl->tracks_iters);
3611 g_hash_table_remove_all(vtl->tracks);
3613 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3615 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3618 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3620 vtl->current_wp = NULL;
3621 vtl->current_wp_id = NULL;
3622 vtl->moving_wp = FALSE;
3624 highest_wp_number_reset(vtl);
3626 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3627 g_hash_table_remove_all(vtl->waypoints_iters);
3628 g_hash_table_remove_all(vtl->waypoints);
3630 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3632 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3635 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3637 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3638 // Get confirmation from the user
3639 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3640 _("Are you sure you want to delete all tracks in %s?"),
3641 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3642 vik_trw_layer_delete_all_tracks (vtl);
3645 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3647 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3648 // Get confirmation from the user
3649 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3650 _("Are you sure you want to delete all waypoints in %s?"),
3651 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3652 vik_trw_layer_delete_all_waypoints (vtl);
3655 static void trw_layer_delete_item ( gpointer pass_along[6] )
3657 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3658 gboolean was_visible = FALSE;
3659 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3661 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3662 if ( wp && wp->name ) {
3663 if ( GPOINTER_TO_INT ( pass_along[4]) )
3664 // Get confirmation from the user
3665 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3666 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3667 _("Are you sure you want to delete the waypoint \"%s\""),
3670 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3675 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3676 if ( trk && trk->name ) {
3677 if ( GPOINTER_TO_INT ( pass_along[4]) )
3678 // Get confirmation from the user
3679 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3680 _("Are you sure you want to delete the track \"%s\""),
3683 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3687 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3691 static void trw_layer_properties_item ( gpointer pass_along[7] )
3693 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3694 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3696 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3698 if ( wp && wp->name )
3700 gboolean updated = FALSE;
3701 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3703 if ( updated && VIK_LAYER(vtl)->visible )
3704 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3709 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3710 if ( tr && tr->name )
3712 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3714 pass_along[1], /* vlp */
3715 pass_along[5] ); /* vvp */
3721 Parameter 1 -> VikLayersPanel
3722 Parameter 2 -> VikLayer
3723 Parameter 3 -> VikViewport
3725 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3728 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3729 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3732 /* since vlp not set, vl & vvp should be valid instead! */
3734 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3735 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3740 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3742 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3743 if ( trps && trps->data )
3744 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3747 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3749 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3750 if ( track && track->trackpoints )
3752 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3754 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
3755 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3756 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3757 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3758 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3762 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3764 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3765 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3767 vtl->current_track = track;
3768 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3770 if ( track->trackpoints )
3771 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3774 #ifdef VIK_CONFIG_GOOGLE
3776 * extend a track using route finder
3778 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3780 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3781 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3782 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3784 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3785 vtl->route_finder_coord = last_coord;
3786 vtl->route_finder_current_track = track;
3787 vtl->route_finder_started = TRUE;
3789 if ( track->trackpoints )
3790 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3795 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3797 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3798 /* Also warn if overwrite old elevation data */
3799 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3801 vik_track_apply_dem_data ( track );
3804 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3806 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3809 trps = g_list_last(trps);
3810 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3813 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3815 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3818 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3821 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3823 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3826 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3829 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3831 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3834 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3838 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3840 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3842 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3843 if ( trk && trk->trackpoints )
3845 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3846 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
3847 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3848 if ( pass_along[1] )
3849 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3851 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3855 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3857 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3858 trw_layer_tpwin_init ( vtl );
3861 /*************************************
3862 * merge/split by time routines
3863 *************************************/
3865 /* called for each key in track hash table.
3866 * If the current track has the same time stamp type, add it to the result,
3867 * except the one pointed by "exclude".
3868 * set exclude to NULL if there is no exclude to check.
3869 * Note that the result is in reverse (for performance reasons).
3874 gboolean with_timestamps;
3876 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3878 twt_udata *user_data = udata;
3879 VikTrackpoint *p1, *p2;
3881 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3885 if (VIK_TRACK(value)->trackpoints) {
3886 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3887 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3889 if ( user_data->with_timestamps ) {
3890 if (!p1->has_timestamp || !p2->has_timestamp) {
3895 // Don't add tracks with timestamps when getting non timestamp tracks
3896 if (p1->has_timestamp || p2->has_timestamp) {
3902 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3905 /* called for each key in track hash table. if original track user_data[1] is close enough
3906 * to the passed one, add it to list in user_data[0]
3908 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
3911 VikTrackpoint *p1, *p2;
3912 VikTrack *trk = VIK_TRACK(value);
3914 GList **nearby_tracks = ((gpointer *)user_data)[0];
3915 GList *tpoints = ((gpointer *)user_data)[1];
3918 * detect reasons for not merging, and return
3919 * if no reason is found not to merge, then do it.
3922 // Exclude the original track from the compiled list
3923 if (trk->trackpoints == tpoints) {
3927 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
3928 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
3930 if (trk->trackpoints) {
3931 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
3932 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
3934 if (!p1->has_timestamp || !p2->has_timestamp) {
3935 //g_print("no timestamp\n");
3939 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3940 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
3941 if (! (abs(t1 - p2->timestamp) < threshold ||
3943 abs(p1->timestamp - t2) < threshold)
3950 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
3953 /* comparison function used to sort tracks; a and b are hash table keys */
3954 /* Not actively used - can be restored if needed
3955 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3957 GHashTable *tracks = user_data;
3960 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3961 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3963 if (t1 < t2) return -1;
3964 if (t1 > t2) return 1;
3969 /* comparison function used to sort trackpoints */
3970 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3972 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3974 if (t1 < t2) return -1;
3975 if (t1 > t2) return 1;
3980 * comparison function which can be used to sort tracks or waypoints by name
3982 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3984 const gchar* namea = (const gchar*) a;
3985 const gchar* nameb = (const gchar*) b;
3986 if ( namea == NULL || nameb == NULL)
3989 // Same sort method as used in the vik_treeview_*_alphabetize functions
3990 return strcmp ( namea, nameb );
3994 * Attempt to merge selected track with other tracks specified by the user
3995 * Tracks to merge with must be of the same 'type' as the selected track -
3996 * either all with timestamps, or all without timestamps
3998 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4000 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4001 GList *other_tracks = NULL;
4002 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4004 if ( !track->trackpoints )
4008 udata.result = &other_tracks;
4009 udata.exclude = track->trackpoints;
4010 // Allow merging with 'similar' time type time tracks
4011 // i.e. either those times, or those without
4012 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4014 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4015 other_tracks = g_list_reverse(other_tracks);
4017 if ( !other_tracks ) {
4018 if ( udata.with_timestamps )
4019 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4021 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4025 // Sort alphabetically for user presentation
4026 // Convert into list of names for usage with dialog function
4027 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4028 GList *other_tracks_names = NULL;
4029 GList *iter = g_list_first ( other_tracks );
4031 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
4032 iter = g_list_next ( iter );
4035 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4037 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4038 other_tracks_names, TRUE,
4039 _("Merge with..."), _("Select track to merge with"));
4040 g_list_free(other_tracks);
4041 g_list_free(other_tracks_names);
4046 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4047 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
4049 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4050 merge_track->trackpoints = NULL;
4051 vik_trw_layer_delete_track (vtl, merge_track);
4052 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4055 /* TODO: free data before free merge_list */
4056 for (l = merge_list; l != NULL; l = g_list_next(l))
4058 g_list_free(merge_list);
4059 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4063 // c.f. trw_layer_sorted_track_id_by_name_list
4064 // but don't add the specified track to the list (normally current track)
4065 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4067 twt_udata *user_data = udata;
4070 if (trk->trackpoints == user_data->exclude) {
4074 // Sort named list alphabetically
4075 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4079 * Join - this allows combining 'routes' and 'tracks'
4080 * i.e. doesn't care about whether tracks have consistent timestamps
4081 * ATM can only append one track at a time to the currently selected track
4083 static void trw_layer_append_track ( gpointer pass_along[6] )
4086 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4087 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4089 GList *other_tracks_names = NULL;
4091 // Sort alphabetically for user presentation
4092 // Convert into list of names for usage with dialog function
4093 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4095 udata.result = &other_tracks_names;
4096 udata.exclude = trk->trackpoints;
4098 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4100 // Note the limit to selecting one track only
4101 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4102 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4103 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4107 _("Select the track to append after the current track"));
4109 g_list_free(other_tracks_names);
4111 // It's a list, but shouldn't contain more than one other track!
4112 if ( append_list ) {
4114 for (l = append_list; l != NULL; l = g_list_next(l)) {
4115 // TODO: at present this uses the first track found by name,
4116 // which with potential multiple same named tracks may not be the one selected...
4117 VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data );
4118 if ( append_track ) {
4119 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4120 append_track->trackpoints = NULL;
4121 vik_trw_layer_delete_track (vtl, append_track);
4124 for (l = append_list; l != NULL; l = g_list_next(l))
4126 g_list_free(append_list);
4127 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4131 /* merge by segments */
4132 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4134 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4135 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4136 guint segments = vik_track_merge_segments ( trk );
4137 // NB currently no need to redraw as segments not actually shown on the display
4138 // However inform the user of what happened:
4140 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4141 g_snprintf(str, 64, tmp_str, segments);
4142 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4145 /* merge by time routine */
4146 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4148 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4152 GList *tracks_with_timestamp = NULL;
4153 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4154 if (orig_trk->trackpoints &&
4155 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4156 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4161 udata.result = &tracks_with_timestamp;
4162 udata.exclude = orig_trk->trackpoints;
4163 udata.with_timestamps = TRUE;
4164 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4165 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4167 if (!tracks_with_timestamp) {
4168 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4171 g_list_free(tracks_with_timestamp);
4173 static guint threshold_in_minutes = 1;
4174 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4175 _("Merge Threshold..."),
4176 _("Merge when time between tracks less than:"),
4177 &threshold_in_minutes)) {
4181 // keep attempting to merge all tracks until no merges within the time specified is possible
4182 gboolean attempt_merge = TRUE;
4183 GList *nearby_tracks = NULL;
4185 static gpointer params[3];
4187 while ( attempt_merge ) {
4189 // Don't try again unless tracks have changed
4190 attempt_merge = FALSE;
4192 trps = orig_trk->trackpoints;
4196 if (nearby_tracks) {
4197 g_list_free(nearby_tracks);
4198 nearby_tracks = NULL;
4201 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4202 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4204 /* g_print("Original track times: %d and %d\n", t1, t2); */
4205 params[0] = &nearby_tracks;
4206 params[1] = (gpointer)trps;
4207 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4209 /* get a list of adjacent-in-time tracks */
4210 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4213 GList *l = nearby_tracks;
4216 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4217 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4219 t1 = get_first_trackpoint(l)->timestamp;
4220 t2 = get_last_trackpoint(l)->timestamp;
4221 #undef get_first_trackpoint
4222 #undef get_last_trackpoint
4223 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4226 /* remove trackpoints from merged track, delete track */
4227 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4228 VIK_TRACK(l->data)->trackpoints = NULL;
4229 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4231 // Tracks have changed, therefore retry again against all the remaining tracks
4232 attempt_merge = TRUE;
4237 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
4240 g_list_free(nearby_tracks);
4241 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4245 * Split a track at the currently selected trackpoint
4247 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl )
4249 if ( !vtl->current_tpl )
4252 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
4253 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4255 VikTrack *tr = vik_track_new ();
4256 GList *newglist = g_list_alloc ();
4257 newglist->prev = NULL;
4258 newglist->next = vtl->current_tpl->next;
4259 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4260 tr->trackpoints = newglist;
4262 vtl->current_tpl->next->prev = newglist; /* end old track here */
4263 vtl->current_tpl->next = NULL;
4265 vtl->current_tpl = newglist; /* change tp to first of new track. */
4266 vtl->current_tp_track = tr;
4270 vik_trw_layer_add_track ( vtl, name, tr );
4276 // Also need id of newly created track
4277 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4278 if ( trkf && udata.uuid )
4279 vtl->current_tp_id = udata.uuid;
4281 vtl->current_tp_id = NULL;
4283 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4288 /* split by time routine */
4289 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
4291 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4292 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4293 GList *trps = track->trackpoints;
4295 GList *newlists = NULL;
4296 GList *newtps = NULL;
4297 static guint thr = 1;
4304 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4305 _("Split Threshold..."),
4306 _("Split when time between trackpoints exceeds:"),
4311 /* iterate through trackpoints, and copy them into new lists without touching original list */
4312 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
4316 ts = VIK_TRACKPOINT(iter->data)->timestamp;
4318 g_print("panic: ts < prev_ts: this should never happen!\n");
4321 if (ts - prev_ts > thr*60) {
4322 /* flush accumulated trackpoints into new list */
4323 newlists = g_list_append(newlists, g_list_reverse(newtps));
4327 /* accumulate trackpoint copies in newtps, in reverse order */
4328 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4330 iter = g_list_next(iter);
4333 newlists = g_list_append(newlists, g_list_reverse(newtps));
4336 /* put lists of trackpoints into tracks */
4338 // Only bother updating if the split results in new tracks
4339 if (g_list_length (newlists) > 1) {
4344 tr = vik_track_new();
4345 tr->visible = track->visible;
4346 tr->trackpoints = (GList *)(iter->data);
4348 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4349 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4350 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
4351 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
4353 iter = g_list_next(iter);
4355 // Remove original track and then update the display
4356 vik_trw_layer_delete_track (vtl, track);
4357 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4359 g_list_free(newlists);
4363 * Split a track by the number of points as specified by the user
4365 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
4367 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4368 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4370 // Check valid track
4371 GList *trps = track->trackpoints;
4375 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4376 _("Split Every Nth Point"),
4377 _("Split on every Nth point:"),
4378 250, // Default value as per typical limited track capacity of various GPS devices
4382 // Was a valid number returned?
4388 GList *newlists = NULL;
4389 GList *newtps = NULL;
4394 /* accumulate trackpoint copies in newtps, in reverse order */
4395 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4397 if (count >= points) {
4398 /* flush accumulated trackpoints into new list */
4399 newlists = g_list_append(newlists, g_list_reverse(newtps));
4403 iter = g_list_next(iter);
4406 // If there is a remaining chunk put that into the new split list
4407 // This may well be the whole track if no split points were encountered
4409 newlists = g_list_append(newlists, g_list_reverse(newtps));
4412 /* put lists of trackpoints into tracks */
4414 // Only bother updating if the split results in new tracks
4415 if (g_list_length (newlists) > 1) {
4420 tr = vik_track_new();
4421 tr->visible = track->visible;
4422 tr->trackpoints = (GList *)(iter->data);
4424 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4425 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4427 iter = g_list_next(iter);
4429 // Remove original track and then update the display
4430 vik_trw_layer_delete_track (vtl, track);
4431 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4433 g_list_free(newlists);
4437 * Split a track at the currently selected trackpoint
4439 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
4441 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4442 trw_layer_split_at_selected_trackpoint ( vtl );
4446 * Split a track by its segments
4448 static void trw_layer_split_segments ( gpointer pass_along[6] )
4450 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4451 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4454 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
4457 for ( i = 0; i < ntracks; i++ ) {
4459 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
4460 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
4465 // Remove original track
4466 vik_trw_layer_delete_track ( vtl, trk );
4467 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4470 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
4473 /* end of split/merge routines */
4476 * Delete adjacent track points at the same position
4477 * AKA Delete Dulplicates on the Properties Window
4479 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
4481 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4482 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4484 gulong removed = vik_track_remove_dup_points ( trk );
4486 // Track has been updated so update tps:
4487 trw_layer_cancel_tps_of_track ( vtl, trk );
4489 // Inform user how much was deleted as it's not obvious from the normal view
4491 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4492 g_snprintf(str, 64, tmp_str, removed);
4493 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4495 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4499 * Delete adjacent track points with the same timestamp
4500 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
4502 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
4504 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4505 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4507 gulong removed = vik_track_remove_same_time_points ( trk );
4509 // Track has been updated so update tps:
4510 trw_layer_cancel_tps_of_track ( vtl, trk );
4512 // Inform user how much was deleted as it's not obvious from the normal view
4514 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4515 g_snprintf(str, 64, tmp_str, removed);
4516 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4518 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4524 static void trw_layer_reverse ( gpointer pass_along[6] )
4526 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4527 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4529 // Check valid track
4530 GList *trps = track->trackpoints;
4534 vik_track_reverse ( track );
4536 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4540 * Similar to trw_layer_enum_item, but this uses a sorted method
4543 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4545 GList **list = (GList**)udata;
4546 // *list = g_list_prepend(*all, key); //unsorted method
4547 // Sort named list alphabetically
4548 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4553 * Now Waypoint specific sort
4555 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4557 GList **list = (GList**)udata;
4558 // Sort named list alphabetically
4559 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4563 * Track specific sort
4565 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4567 GList **list = (GList**)udata;
4568 // Sort named list alphabetically
4569 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4574 gboolean has_same_track_name;
4575 const gchar *same_track_name;
4576 } same_track_name_udata;
4578 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4580 const gchar* namea = (const gchar*) aa;
4581 const gchar* nameb = (const gchar*) bb;
4584 gint result = strcmp ( namea, nameb );
4586 if ( result == 0 ) {
4587 // Found two names the same
4588 same_track_name_udata *user_data = udata;
4589 user_data->has_same_track_name = TRUE;
4590 user_data->same_track_name = namea;
4593 // Leave ordering the same
4598 * Find out if any tracks have the same name in this layer
4600 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4602 // Sort items by name, then compare if any next to each other are the same
4604 GList *track_names = NULL;
4605 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4608 if ( ! track_names )
4611 same_track_name_udata udata;
4612 udata.has_same_track_name = FALSE;
4614 // Use sort routine to traverse list comparing items
4615 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4616 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4617 // Still no tracks...
4621 return udata.has_same_track_name;
4625 * Force unqiue track names for this layer
4626 * Note the panel is a required parameter to enable the update of the names displayed
4628 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4630 // . Search list for an instance of repeated name
4631 // . get track of this name
4632 // . create new name
4633 // . rename track & update equiv. treeview iter
4634 // . repeat until all different
4636 same_track_name_udata udata;
4638 GList *track_names = NULL;
4639 udata.has_same_track_name = FALSE;
4640 udata.same_track_name = NULL;
4642 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4645 if ( ! track_names )
4648 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4650 // Still no tracks...
4651 if ( ! dummy_list1 )
4654 while ( udata.has_same_track_name ) {
4656 // Find a track with the same name
4657 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4661 g_critical("Houston, we've had a problem.");
4662 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4663 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4668 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4669 vik_track_set_name ( trk, newname );
4675 // Need want key of it for treeview update
4676 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4678 if ( trkf && udataU.uuid ) {
4680 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4683 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4684 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4685 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4690 // Start trying to find same names again...
4692 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4693 udata.has_same_track_name = FALSE;
4694 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4696 // No tracks any more - give up searching
4697 if ( ! dummy_list2 )
4698 udata.has_same_track_name = FALSE;
4702 vik_layers_panel_emit_update ( vlp );
4708 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4710 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4713 // Ensure list of track names offered is unique
4714 if ( trw_layer_has_same_track_names ( vtl ) ) {
4715 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4716 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4717 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4723 // Sort list alphabetically for better presentation
4724 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4727 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4731 // Get list of items to delete from the user
4732 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4735 _("Delete Selection"),
4736 _("Select tracks to delete"));
4739 // Delete requested tracks
4740 // since specificly requested, IMHO no need for extra confirmation
4741 if ( delete_list ) {
4743 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4744 // This deletes first trk it finds of that name (but uniqueness is enforced above)
4745 trw_layer_delete_track_by_name (vtl, l->data);
4747 g_list_free(delete_list);
4748 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4753 gboolean has_same_waypoint_name;
4754 const gchar *same_waypoint_name;
4755 } same_waypoint_name_udata;
4757 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4759 const gchar* namea = (const gchar*) aa;
4760 const gchar* nameb = (const gchar*) bb;
4763 gint result = strcmp ( namea, nameb );
4765 if ( result == 0 ) {
4766 // Found two names the same
4767 same_waypoint_name_udata *user_data = udata;
4768 user_data->has_same_waypoint_name = TRUE;
4769 user_data->same_waypoint_name = namea;
4772 // Leave ordering the same
4777 * Find out if any waypoints have the same name in this layer
4779 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
4781 // Sort items by name, then compare if any next to each other are the same
4783 GList *waypoint_names = NULL;
4784 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4787 if ( ! waypoint_names )
4790 same_waypoint_name_udata udata;
4791 udata.has_same_waypoint_name = FALSE;
4793 // Use sort routine to traverse list comparing items
4794 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4795 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4796 // Still no waypoints...
4800 return udata.has_same_waypoint_name;
4804 * Force unqiue waypoint names for this layer
4805 * Note the panel is a required parameter to enable the update of the names displayed
4807 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4809 // . Search list for an instance of repeated name
4810 // . get waypoint of this name
4811 // . create new name
4812 // . rename waypoint & update equiv. treeview iter
4813 // . repeat until all different
4815 same_waypoint_name_udata udata;
4817 GList *waypoint_names = NULL;
4818 udata.has_same_waypoint_name = FALSE;
4819 udata.same_waypoint_name = NULL;
4821 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4824 if ( ! waypoint_names )
4827 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4829 // Still no waypoints...
4830 if ( ! dummy_list1 )
4833 while ( udata.has_same_waypoint_name ) {
4835 // Find a waypoint with the same name
4836 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
4840 g_critical("Houston, we've had a problem.");
4841 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4842 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
4847 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
4848 vik_waypoint_set_name ( waypoint, newname );
4851 udataU.wp = waypoint;
4854 // Need want key of it for treeview update
4855 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4857 if ( wpf && udataU.uuid ) {
4859 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4862 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4863 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4864 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4869 // Start trying to find same names again...
4870 waypoint_names = NULL;
4871 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4872 udata.has_same_waypoint_name = FALSE;
4873 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4875 // No waypoints any more - give up searching
4876 if ( ! dummy_list2 )
4877 udata.has_same_waypoint_name = FALSE;
4881 vik_layers_panel_emit_update ( vlp );
4887 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4889 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4892 // Ensure list of waypoint names offered is unique
4893 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
4894 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4895 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4896 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4902 // Sort list alphabetically for better presentation
4903 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4905 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4909 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4911 // Get list of items to delete from the user
4912 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4915 _("Delete Selection"),
4916 _("Select waypoints to delete"));
4919 // Delete requested waypoints
4920 // since specificly requested, IMHO no need for extra confirmation
4921 if ( delete_list ) {
4923 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4924 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
4925 trw_layer_delete_waypoint_by_name (vtl, l->data);
4927 g_list_free(delete_list);
4928 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4933 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4935 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4937 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4940 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4942 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4943 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4947 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4949 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4951 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
4953 // No actual change to the name supplied
4954 if (strcmp(newname, wp->name) == 0 )
4957 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4960 // An existing waypoint has been found with the requested name
4961 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4962 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
4967 // Update WP name and refresh the treeview
4968 vik_waypoint_set_name (wp, newname);
4970 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4971 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4974 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4979 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4981 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
4983 // No actual change to the name supplied
4984 if (strcmp(newname, trk->name) == 0)
4987 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4990 // An existing track has been found with the requested name
4991 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4992 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4996 // Update track name and refresh GUI parts
4997 vik_track_set_name (trk, newname);
4999 // Update any subwindows that could be displaying this track which has changed name
5000 // Only one Track Edit Window
5001 if ( l->current_tp_track == trk && l->tpwin ) {
5002 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5004 // Property Dialog of the track
5005 vik_trw_layer_propwin_update ( trk );
5007 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5008 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5011 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5018 static gboolean is_valid_geocache_name ( gchar *str )
5020 gint len = strlen ( str );
5021 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]));
5024 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
5026 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5027 a_acquire_set_filter_track ( trk );
5030 #ifdef VIK_CONFIG_GOOGLE
5031 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
5033 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
5034 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
5037 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
5039 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5041 gchar *escaped = uri_escape ( tr->comment );
5042 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
5043 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5050 /* vlp can be NULL if necessary - i.e. right-click from a tool */
5051 /* viewpoint is now available instead */
5052 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
5054 static gpointer pass_along[8];
5056 gboolean rv = FALSE;
5059 pass_along[1] = vlp;
5060 pass_along[2] = GINT_TO_POINTER (subtype);
5061 pass_along[3] = sublayer;
5062 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5063 pass_along[5] = vvp;
5064 pass_along[6] = iter;
5065 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
5067 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5071 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5073 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5074 gtk_widget_show ( item );
5076 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5077 VikTrwLayer *vtl = l;
5078 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
5079 if (tr && tr->property_dialog)
5080 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5083 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5084 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5085 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5086 gtk_widget_show ( item );
5088 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
5089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
5090 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5091 gtk_widget_show ( item );
5093 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
5094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
5095 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5096 gtk_widget_show ( item );
5098 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5100 gboolean separator_created = FALSE;
5102 /* could be a right-click using the tool */
5103 if ( vlp != NULL ) {
5104 item = gtk_menu_item_new ();
5105 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5106 gtk_widget_show ( item );
5108 separator_created = TRUE;
5110 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5111 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
5113 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5114 gtk_widget_show ( item );
5117 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5119 if ( wp && wp->name ) {
5120 if ( is_valid_geocache_name ( wp->name ) ) {
5122 if ( !separator_created ) {
5123 item = gtk_menu_item_new ();
5124 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5125 gtk_widget_show ( item );
5126 separator_created = TRUE;
5129 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
5130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
5131 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5132 gtk_widget_show ( item );
5136 if ( wp && wp->image )
5138 if ( !separator_created ) {
5139 item = gtk_menu_item_new ();
5140 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5141 gtk_widget_show ( item );
5142 separator_created = TRUE;
5145 // Set up image paramater
5146 pass_along[5] = wp->image;
5148 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
5149 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
5150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
5151 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5152 gtk_widget_show ( item );
5154 #ifdef VIK_CONFIG_GEOTAG
5155 GtkWidget *geotag_submenu = gtk_menu_new ();
5156 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
5157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
5158 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5159 gtk_widget_show ( item );
5160 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
5162 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
5163 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
5164 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5165 gtk_widget_show ( item );
5167 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
5168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
5169 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5170 gtk_widget_show ( item );
5177 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
5180 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
5181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
5183 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5184 gtk_widget_show ( item );
5187 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
5189 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
5190 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5191 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
5192 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5193 gtk_widget_show ( item );
5195 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
5196 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5197 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
5198 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5199 gtk_widget_show ( item );
5201 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
5202 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
5204 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5205 gtk_widget_show ( item );
5207 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
5208 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5209 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
5210 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5211 gtk_widget_show ( item );
5214 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
5218 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
5219 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
5221 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5222 gtk_widget_show ( item );
5224 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
5225 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5226 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
5227 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5228 gtk_widget_show ( item );
5230 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
5231 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
5233 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5234 gtk_widget_show ( item );
5237 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5239 item = gtk_menu_item_new ();
5240 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5241 gtk_widget_show ( item );
5243 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
5244 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
5246 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5247 gtk_widget_show ( item );
5249 GtkWidget *goto_submenu;
5250 goto_submenu = gtk_menu_new ();
5251 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5253 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5254 gtk_widget_show ( item );
5255 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
5257 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
5258 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
5259 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
5260 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5261 gtk_widget_show ( item );
5263 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
5264 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
5266 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5267 gtk_widget_show ( item );
5269 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
5270 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
5271 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
5272 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5273 gtk_widget_show ( item );
5275 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
5276 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
5277 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
5278 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5279 gtk_widget_show ( item );
5281 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
5282 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
5283 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
5284 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5285 gtk_widget_show ( item );
5287 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
5288 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
5289 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
5290 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5291 gtk_widget_show ( item );
5293 GtkWidget *combine_submenu;
5294 combine_submenu = gtk_menu_new ();
5295 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
5296 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
5297 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5298 gtk_widget_show ( item );
5299 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
5301 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
5302 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
5303 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5304 gtk_widget_show ( item );
5306 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
5307 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
5308 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5309 gtk_widget_show ( item );
5311 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
5312 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
5313 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5314 gtk_widget_show ( item );
5316 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
5317 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
5318 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5319 gtk_widget_show ( item );
5321 GtkWidget *split_submenu;
5322 split_submenu = gtk_menu_new ();
5323 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
5324 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
5325 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5326 gtk_widget_show ( item );
5327 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
5329 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
5330 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
5331 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5332 gtk_widget_show ( item );
5334 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
5335 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
5336 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5337 gtk_widget_show ( item );
5339 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
5340 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
5341 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
5342 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5343 gtk_widget_show ( item );
5345 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
5346 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
5347 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5348 gtk_widget_show ( item );
5349 // Make it available only when a trackpoint is selected.
5350 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
5352 GtkWidget *delete_submenu;
5353 delete_submenu = gtk_menu_new ();
5354 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
5355 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
5356 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5357 gtk_widget_show ( item );
5358 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
5360 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
5361 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
5362 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5363 gtk_widget_show ( item );
5365 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
5366 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
5367 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5368 gtk_widget_show ( item );
5370 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
5371 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
5372 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
5373 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5374 gtk_widget_show ( item );
5376 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
5378 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
5379 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
5380 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
5381 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5382 gtk_widget_show ( item );
5385 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
5386 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
5387 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
5388 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5389 gtk_widget_show ( item );
5391 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
5392 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
5393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
5394 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5395 gtk_widget_show ( item );
5397 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
5398 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
5399 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
5400 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5401 gtk_widget_show ( item );
5403 #ifdef VIK_CONFIG_GOOGLE
5404 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
5405 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
5406 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
5407 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5408 gtk_widget_show ( item );
5411 GtkWidget *upload_submenu = gtk_menu_new ();
5412 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
5413 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5414 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5415 gtk_widget_show ( item );
5416 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
5418 #ifdef VIK_CONFIG_OPENSTREETMAP
5419 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
5420 // Convert internal pointer into actual track for usage outside this file
5421 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
5422 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5423 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
5424 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5425 gtk_widget_show ( item );
5428 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
5429 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
5430 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
5431 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5432 gtk_widget_show ( item );
5434 #ifdef VIK_CONFIG_GOOGLE
5435 if ( is_valid_google_route ( l, sublayer ) )
5437 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
5438 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
5439 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
5440 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5441 gtk_widget_show ( item );
5445 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
5446 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
5448 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5449 gtk_widget_show ( item );
5451 /* ATM This function is only available via the layers panel, due to needing a vlp */
5453 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
5454 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
5455 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
5457 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5458 gtk_widget_show ( item );
5462 #ifdef VIK_CONFIG_GEOTAG
5463 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
5464 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
5465 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5466 gtk_widget_show ( item );
5469 // Only show on viewport popmenu when a trackpoint is selected
5470 if ( ! vlp && l->current_tpl ) {
5472 item = gtk_menu_item_new ();
5473 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5474 gtk_widget_show ( item );
5476 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
5477 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
5478 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
5479 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5480 gtk_widget_show ( item );
5488 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
5491 if (!vtl->current_tpl)
5493 if (!vtl->current_tpl->next)
5496 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
5497 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
5499 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
5502 VikTrackpoint *tp_new = vik_trackpoint_new();
5503 struct LatLon ll_current, ll_next;
5504 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
5505 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
5507 /* main positional interpolation */
5508 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
5509 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
5511 /* Now other properties that can be interpolated */
5512 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
5514 if (tp_current->has_timestamp && tp_next->has_timestamp) {
5515 /* Note here the division is applied to each part, then added
5516 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
5517 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
5518 tp_new->has_timestamp = TRUE;
5521 if (tp_current->speed != NAN && tp_next->speed != NAN)
5522 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
5524 /* TODO - improve interpolation of course, as it may not be correct.
5525 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
5526 [similar applies if value is in radians] */
5527 if (tp_current->course != NAN && tp_next->course != NAN)
5528 tp_new->speed = (tp_current->course + tp_next->course)/2;
5530 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
5532 /* Insert new point into the trackpoints list after the current TP */
5533 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5534 gint index = g_list_index ( tr->trackpoints, tp_current );
5536 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
5541 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
5547 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
5551 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
5553 if ( vtl->current_tpl )
5555 vtl->current_tpl = NULL;
5556 vtl->current_tp_track = NULL;
5557 vtl->current_tp_id = NULL;
5558 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5562 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
5564 g_assert ( vtl->tpwin != NULL );
5565 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
5566 trw_layer_cancel_current_tp ( vtl, TRUE );
5568 if ( vtl->current_tpl == NULL )
5571 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
5573 trw_layer_split_at_selected_trackpoint ( vtl );
5574 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5576 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5578 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5584 // Find available adjacent trackpoint
5585 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5587 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5588 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5590 // Delete current trackpoint
5591 vik_trackpoint_free ( vtl->current_tpl->data );
5592 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5594 // Set to current to the available adjacent trackpoint
5595 vtl->current_tpl = new_tpl;
5597 // Reset dialog with the available adjacent trackpoint
5598 if ( vtl->current_tp_track )
5599 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
5601 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5605 // Delete current trackpoint
5606 vik_trackpoint_free ( vtl->current_tpl->data );
5607 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5608 trw_layer_cancel_current_tp ( vtl, FALSE );
5611 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5613 if ( vtl->current_tp_track )
5614 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
5615 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
5617 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5619 if ( vtl->current_tp_track )
5620 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
5621 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5623 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5625 trw_layer_insert_tp_after_current_tp ( vtl );
5626 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5628 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
5629 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5632 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5636 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5637 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5638 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5639 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5640 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5642 if ( vtl->current_tpl )
5643 if ( vtl->current_tp_track )
5644 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5645 /* set layer name and TP data */
5648 /***************************************************************************
5650 ***************************************************************************/
5652 /*** Utility data structures and functions ****/
5656 gint closest_x, closest_y;
5657 gpointer *closest_wp_id;
5658 VikWaypoint *closest_wp;
5664 gint closest_x, closest_y;
5665 gpointer closest_track_id;
5666 VikTrackpoint *closest_tp;
5671 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5677 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5679 // If waypoint has an image then use the image size to select
5681 gint slackx, slacky;
5682 slackx = wp->image_width / 2;
5683 slacky = wp->image_height / 2;
5685 if ( x <= params->x + slackx && x >= params->x - slackx
5686 && y <= params->y + slacky && y >= params->y - slacky ) {
5687 params->closest_wp_id = id;
5688 params->closest_wp = wp;
5689 params->closest_x = x;
5690 params->closest_y = y;
5693 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5694 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5695 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5697 params->closest_wp_id = id;
5698 params->closest_wp = wp;
5699 params->closest_x = x;
5700 params->closest_y = y;
5704 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5706 GList *tpl = t->trackpoints;
5715 tp = VIK_TRACKPOINT(tpl->data);
5717 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5719 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5720 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5721 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5723 params->closest_track_id = id;
5724 params->closest_tp = tp;
5725 params->closest_tpl = tpl;
5726 params->closest_x = x;
5727 params->closest_y = y;
5733 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5735 TPSearchParams params;
5739 params.closest_track_id = NULL;
5740 params.closest_tp = NULL;
5741 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5742 return params.closest_tp;
5745 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5747 WPSearchParams params;
5751 params.closest_wp = NULL;
5752 params.closest_wp_id = NULL;
5753 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5754 return params.closest_wp;
5758 // Some forward declarations
5759 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5760 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5761 static void marker_end_move ( tool_ed_t *t );
5764 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5768 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5770 // Here always allow snapping back to the original location
5771 // this is useful when one decides not to move the thing afterall
5772 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5775 if ( event->state & GDK_CONTROL_MASK )
5777 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5779 new_coord = tp->coord;
5783 if ( event->state & GDK_SHIFT_MASK )
5785 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5787 new_coord = wp->coord;
5791 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5793 marker_moveto ( t, x, y );
5800 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5802 if ( t->holding && event->button == 1 )
5805 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5808 if ( event->state & GDK_CONTROL_MASK )
5810 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5812 new_coord = tp->coord;
5816 if ( event->state & GDK_SHIFT_MASK )
5818 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5820 new_coord = wp->coord;
5823 marker_end_move ( t );
5825 // Determine if working on a waypoint or a trackpoint
5826 if ( t->is_waypoint )
5827 vtl->current_wp->coord = new_coord;
5829 if ( vtl->current_tpl ) {
5830 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5833 if ( vtl->current_tp_track )
5834 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5839 vtl->current_wp = NULL;
5840 vtl->current_wp_id = NULL;
5841 trw_layer_cancel_current_tp ( vtl, FALSE );
5843 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5850 Returns true if a waypoint or track is found near the requested event position for this particular layer
5851 The item found is automatically selected
5852 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5854 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5856 if ( event->button != 1 )
5859 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5862 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5865 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5867 if (vtl->waypoints_visible) {
5868 WPSearchParams wp_params;
5869 wp_params.vvp = vvp;
5870 wp_params.x = event->x;
5871 wp_params.y = event->y;
5872 wp_params.closest_wp_id = NULL;
5873 wp_params.closest_wp = NULL;
5875 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5877 if ( wp_params.closest_wp ) {
5880 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5882 // Too easy to move it so must be holding shift to start immediately moving it
5883 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5884 if ( event->state & GDK_SHIFT_MASK ||
5885 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5886 // Put into 'move buffer'
5887 // NB vvp & vw already set in tet
5888 tet->vtl = (gpointer)vtl;
5889 tet->is_waypoint = TRUE;
5891 marker_begin_move (tet, event->x, event->y);
5894 vtl->current_wp = wp_params.closest_wp;
5895 vtl->current_wp_id = wp_params.closest_wp_id;
5897 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5903 if (vtl->tracks_visible) {
5904 TPSearchParams tp_params;
5905 tp_params.vvp = vvp;
5906 tp_params.x = event->x;
5907 tp_params.y = event->y;
5908 tp_params.closest_track_id = NULL;
5909 tp_params.closest_tp = NULL;
5911 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5913 if ( tp_params.closest_tp ) {
5915 // Always select + highlight the track
5916 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5918 tet->is_waypoint = FALSE;
5920 // Select the Trackpoint
5921 // Can move it immediately when control held or it's the previously selected tp
5922 if ( event->state & GDK_CONTROL_MASK ||
5923 vtl->current_tpl == tp_params.closest_tpl ) {
5924 // Put into 'move buffer'
5925 // NB vvp & vw already set in tet
5926 tet->vtl = (gpointer)vtl;
5927 marker_begin_move (tet, event->x, event->y);
5930 vtl->current_tpl = tp_params.closest_tpl;
5931 vtl->current_tp_id = tp_params.closest_track_id;
5932 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5934 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5937 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5939 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5944 /* these aren't the droids you're looking for */
5945 vtl->current_wp = NULL;
5946 vtl->current_wp_id = NULL;
5947 trw_layer_cancel_current_tp ( vtl, FALSE );
5950 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5955 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5957 if ( event->button != 3 )
5960 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5963 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5966 /* Post menu for the currently selected item */
5968 /* See if a track is selected */
5969 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5970 if ( track && track->visible ) {
5972 if ( track->name ) {
5974 if ( vtl->track_right_click_menu )
5975 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5977 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5983 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5985 if ( trkf && udataU.uuid ) {
5987 GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5989 trw_layer_sublayer_add_menu_items ( vtl,
5990 vtl->track_right_click_menu,
5992 VIK_TRW_LAYER_SUBLAYER_TRACK,
5998 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6004 /* See if a waypoint is selected */
6005 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6006 if ( waypoint && waypoint->visible ) {
6007 if ( waypoint->name ) {
6009 if ( vtl->wp_right_click_menu )
6010 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
6012 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
6015 udata.wp = waypoint;
6018 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
6020 if ( wpf && udata.uuid ) {
6021 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
6023 trw_layer_sublayer_add_menu_items ( vtl,
6024 vtl->wp_right_click_menu,
6026 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
6031 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6040 /* background drawing hook, to be passed the viewport */
6041 static gboolean tool_sync_done = TRUE;
6043 static gboolean tool_sync(gpointer data)
6045 VikViewport *vvp = data;
6046 gdk_threads_enter();
6047 vik_viewport_sync(vvp);
6048 tool_sync_done = TRUE;
6049 gdk_threads_leave();
6053 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
6056 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
6057 gdk_gc_set_function ( t->gc, GDK_INVERT );
6058 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6059 vik_viewport_sync(t->vvp);
6064 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
6066 VikViewport *vvp = t->vvp;
6067 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6068 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6072 if (tool_sync_done) {
6073 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
6074 tool_sync_done = FALSE;
6078 static void marker_end_move ( tool_ed_t *t )
6080 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6081 g_object_unref ( t->gc );
6085 /*** Edit waypoint ****/
6087 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6089 tool_ed_t *t = g_new(tool_ed_t, 1);
6095 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6097 WPSearchParams params;
6098 tool_ed_t *t = data;
6099 VikViewport *vvp = t->vvp;
6101 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6108 if ( !vtl->vl.visible || !vtl->waypoints_visible )
6111 if ( vtl->current_wp && vtl->current_wp->visible )
6113 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
6115 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
6117 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
6118 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
6120 if ( event->button == 3 )
6121 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6123 marker_begin_move(t, event->x, event->y);
6130 params.x = event->x;
6131 params.y = event->y;
6132 params.closest_wp_id = NULL;
6133 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6134 params.closest_wp = NULL;
6135 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6136 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
6138 // how do we get here?
6139 marker_begin_move(t, event->x, event->y);
6140 g_critical("shouldn't be here");
6143 else if ( params.closest_wp )
6145 if ( event->button == 3 )
6146 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6148 vtl->waypoint_rightclick = FALSE;
6150 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
6152 vtl->current_wp = params.closest_wp;
6153 vtl->current_wp_id = params.closest_wp_id;
6155 /* could make it so don't update if old WP is off screen and new is null but oh well */
6156 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6160 vtl->current_wp = NULL;
6161 vtl->current_wp_id = NULL;
6162 vtl->waypoint_rightclick = FALSE;
6163 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6167 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6169 tool_ed_t *t = data;
6170 VikViewport *vvp = t->vvp;
6172 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6177 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6180 if ( event->state & GDK_CONTROL_MASK )
6182 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6184 new_coord = tp->coord;
6188 if ( event->state & GDK_SHIFT_MASK )
6190 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6191 if ( wp && wp != vtl->current_wp )
6192 new_coord = wp->coord;
6197 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6199 marker_moveto ( t, x, y );
6206 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6208 tool_ed_t *t = data;
6209 VikViewport *vvp = t->vvp;
6211 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6214 if ( t->holding && event->button == 1 )
6217 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6220 if ( event->state & GDK_CONTROL_MASK )
6222 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6224 new_coord = tp->coord;
6228 if ( event->state & GDK_SHIFT_MASK )
6230 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6231 if ( wp && wp != vtl->current_wp )
6232 new_coord = wp->coord;
6235 marker_end_move ( t );
6237 vtl->current_wp->coord = new_coord;
6238 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6241 /* PUT IN RIGHT PLACE!!! */
6242 if ( event->button == 3 && vtl->waypoint_rightclick )
6244 if ( vtl->wp_right_click_menu )
6245 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
6246 if ( vtl->current_wp ) {
6247 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
6248 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 );
6249 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6251 vtl->waypoint_rightclick = FALSE;
6256 /**** Begin track ***/
6257 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
6262 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6264 vtl->current_track = NULL;
6265 return tool_new_track_click ( vtl, event, vvp );
6268 /*** New track ****/
6270 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
6277 GdkDrawable *drawable;
6283 * Draw specified pixmap
6285 static gboolean draw_sync ( gpointer data )
6287 draw_sync_t *ds = (draw_sync_t*) data;
6288 // Sometimes don't want to draw
6289 // normally because another update has taken precedent such as panning the display
6290 // which means this pixmap is no longer valid
6291 if ( ds->vtl->draw_sync_do ) {
6292 gdk_threads_enter();
6293 gdk_draw_drawable (ds->drawable,
6296 0, 0, 0, 0, -1, -1);
6297 ds->vtl->draw_sync_done = TRUE;
6298 gdk_threads_leave();
6303 static gchar* distance_string (gdouble distance)
6307 /* draw label with distance */
6308 vik_units_distance_t dist_units = a_vik_get_units_distance ();
6309 switch (dist_units) {
6310 case VIK_UNITS_DISTANCE_MILES:
6311 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
6312 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
6313 } else if (distance < 1609.4) {
6314 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
6316 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
6320 // VIK_UNITS_DISTANCE_KILOMETRES
6321 if (distance >= 1000 && distance < 100000) {
6322 g_sprintf(str, "%3.2f km", distance/1000.0);
6323 } else if (distance < 1000) {
6324 g_sprintf(str, "%d m", (int)distance);
6326 g_sprintf(str, "%d km", (int)distance/1000);
6330 return g_strdup (str);
6334 * Actually set the message in statusbar
6336 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
6338 // Only show elevation data when track has some elevation properties
6339 gchar str_gain_loss[64];
6340 str_gain_loss[0] = '\0';
6342 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
6343 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
6344 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
6346 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
6349 // Write with full gain/loss information
6350 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
6351 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
6356 * Figure out what information should be set in the statusbar and then write it
6358 static void update_statusbar ( VikTrwLayer *vtl )
6360 // Get elevation data
6361 gdouble elev_gain, elev_loss;
6362 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6364 /* Find out actual distance of current track */
6365 gdouble distance = vik_track_get_length (vtl->current_track);
6366 gchar *str = distance_string (distance);
6368 statusbar_write (str, elev_gain, elev_loss, vtl);
6374 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
6376 /* if we haven't sync'ed yet, we don't have time to do more. */
6377 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
6378 GList *iter = g_list_last ( vtl->current_track->trackpoints );
6379 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
6381 static GdkPixmap *pixmap = NULL;
6383 // Need to check in case window has been resized
6384 w1 = vik_viewport_get_width(vvp);
6385 h1 = vik_viewport_get_height(vvp);
6387 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6389 gdk_drawable_get_size (pixmap, &w2, &h2);
6390 if (w1 != w2 || h1 != h2) {
6391 g_object_unref ( G_OBJECT ( pixmap ) );
6392 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6395 // Reset to background
6396 gdk_draw_drawable (pixmap,
6397 vtl->current_track_newpoint_gc,
6398 vik_viewport_get_pixmap(vvp),
6399 0, 0, 0, 0, -1, -1);
6401 draw_sync_t *passalong;
6404 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
6406 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
6407 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
6408 // thus when we come to reset to the background it would include what we have already drawn!!
6409 gdk_draw_line ( pixmap,
6410 vtl->current_track_newpoint_gc,
6411 x1, y1, event->x, event->y );
6412 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
6414 /* Find out actual distance of current track */
6415 gdouble distance = vik_track_get_length (vtl->current_track);
6417 // Now add distance to where the pointer is //
6420 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
6421 vik_coord_to_latlon ( &coord, &ll );
6422 distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
6424 // Get elevation data
6425 gdouble elev_gain, elev_loss;
6426 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6428 // Adjust elevation data (if available) for the current pointer position
6430 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
6431 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
6432 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
6433 // Adjust elevation of last track point
6434 if ( elev_new > last_tpt->altitude )
6436 elev_gain += elev_new - last_tpt->altitude;
6439 elev_loss += last_tpt->altitude - elev_new;
6443 gchar *str = distance_string (distance);
6445 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
6446 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
6448 pango_layout_set_text (pl, str, -1);
6450 pango_layout_get_pixel_size ( pl, &wd, &hd );
6453 // offset from cursor a bit depending on font size
6457 // Create a background block to make the text easier to read over the background map
6458 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
6459 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
6460 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
6462 g_object_unref ( G_OBJECT ( pl ) );
6463 g_object_unref ( G_OBJECT ( background_block_gc ) );
6465 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
6466 passalong->vtl = vtl;
6467 passalong->pixmap = pixmap;
6468 passalong->drawable = GTK_WIDGET(vvp)->window;
6469 passalong->gc = vtl->current_track_newpoint_gc;
6471 // Update statusbar with full gain/loss information
6472 statusbar_write (str, elev_gain, elev_loss, vtl);
6476 // draw pixmap when we have time to
6477 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
6478 vtl->draw_sync_done = FALSE;
6479 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
6481 return VIK_LAYER_TOOL_ACK;
6484 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
6486 if ( vtl->current_track && event->keyval == GDK_Escape ) {
6487 vtl->current_track = NULL;
6488 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6490 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
6492 if ( vtl->current_track->trackpoints )
6494 GList *last = g_list_last(vtl->current_track->trackpoints);
6495 g_free ( last->data );
6496 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6499 update_statusbar ( vtl );
6501 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6507 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6511 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6514 if ( event->button == 2 ) {
6515 // As the display is panning, the new track pixmap is now invalid so don't draw it
6516 // otherwise this drawing done results in flickering back to an old image
6517 vtl->draw_sync_do = FALSE;
6521 if ( event->button == 3 && vtl->current_track )
6524 if ( vtl->current_track->trackpoints )
6526 GList *last = g_list_last(vtl->current_track->trackpoints);
6527 g_free ( last->data );
6528 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6530 update_statusbar ( vtl );
6532 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6536 if ( event->type == GDK_2BUTTON_PRESS )
6538 /* subtract last (duplicate from double click) tp then end */
6539 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
6541 GList *last = g_list_last(vtl->current_track->trackpoints);
6542 g_free ( last->data );
6543 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6544 /* undo last, then end */
6545 vtl->current_track = NULL;
6547 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6551 if ( ! vtl->current_track )
6553 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
6554 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
6556 vtl->current_track = vik_track_new();
6557 vtl->current_track->visible = TRUE;
6558 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
6560 /* incase it was created by begin track */
6561 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
6566 tp = vik_trackpoint_new();
6567 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
6569 /* snap to other TP */
6570 if ( event->state & GDK_CONTROL_MASK )
6572 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6574 tp->coord = other_tp->coord;
6577 tp->newsegment = FALSE;
6578 tp->has_timestamp = FALSE;
6580 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
6581 /* Auto attempt to get elevation from DEM data (if it's available) */
6582 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
6584 vtl->ct_x1 = vtl->ct_x2;
6585 vtl->ct_y1 = vtl->ct_y2;
6586 vtl->ct_x2 = event->x;
6587 vtl->ct_y2 = event->y;
6589 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6593 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6595 if ( event->button == 2 ) {
6596 // Pan moving ended - enable potential point drawing again
6597 vtl->draw_sync_do = TRUE;
6598 vtl->draw_sync_done = TRUE;
6602 /*** New waypoint ****/
6604 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6609 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6612 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6614 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6615 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
6616 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6621 /*** Edit trackpoint ****/
6623 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6625 tool_ed_t *t = g_new(tool_ed_t, 1);
6631 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6633 tool_ed_t *t = data;
6634 VikViewport *vvp = t->vvp;
6635 TPSearchParams params;
6636 /* OUTDATED DOCUMENTATION:
6637 find 5 pixel range on each side. then put these UTM, and a pointer
6638 to the winning track name (and maybe the winning track itself), and a
6639 pointer to the winning trackpoint, inside an array or struct. pass
6640 this along, do a foreach on the tracks which will do a foreach on the
6643 params.x = event->x;
6644 params.y = event->y;
6645 params.closest_track_id = NULL;
6646 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6647 params.closest_tp = NULL;
6649 if ( event->button != 1 )
6652 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6655 if ( !vtl->vl.visible || !vtl->tracks_visible )
6658 if ( vtl->current_tpl )
6660 /* 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.) */
6661 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6662 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6667 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6669 if ( current_tr->visible &&
6670 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
6671 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6672 marker_begin_move ( t, event->x, event->y );
6678 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6680 if ( params.closest_tp )
6682 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
6683 vtl->current_tpl = params.closest_tpl;
6684 vtl->current_tp_id = params.closest_track_id;
6685 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
6686 trw_layer_tpwin_init ( vtl );
6687 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
6688 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6692 /* these aren't the droids you're looking for */
6696 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6698 tool_ed_t *t = data;
6699 VikViewport *vvp = t->vvp;
6701 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6707 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6710 if ( event->state & GDK_CONTROL_MASK )
6712 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6713 if ( tp && tp != vtl->current_tpl->data )
6714 new_coord = tp->coord;
6716 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6719 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6720 marker_moveto ( t, x, y );
6728 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6730 tool_ed_t *t = data;
6731 VikViewport *vvp = t->vvp;
6733 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6735 if ( event->button != 1)
6740 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6743 if ( event->state & GDK_CONTROL_MASK )
6745 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6746 if ( tp && tp != vtl->current_tpl->data )
6747 new_coord = tp->coord;
6750 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6752 marker_end_move ( t );
6754 /* diff dist is diff from orig */
6756 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6758 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6765 #ifdef VIK_CONFIG_GOOGLE
6766 /*** Route Finder ***/
6767 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6772 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6775 if ( !vtl ) return FALSE;
6776 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6777 if ( event->button == 3 && vtl->route_finder_current_track ) {
6779 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6781 vtl->route_finder_coord = *new_end;
6783 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6784 /* remove last ' to:...' */
6785 if ( vtl->route_finder_current_track->comment ) {
6786 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6787 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6788 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6789 last_to - vtl->route_finder_current_track->comment - 1);
6790 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6795 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6796 struct LatLon start, end;
6797 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6798 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6801 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6802 vik_coord_to_latlon ( &(tmp), &end );
6803 vtl->route_finder_coord = tmp; /* for continuations */
6805 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6806 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6807 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6809 vtl->route_finder_check_added_track = TRUE;
6810 vtl->route_finder_started = FALSE;
6813 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6814 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6815 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6816 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6817 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6818 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
6821 /* see if anything was done -- a track was added or appended to */
6822 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6823 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 ) );
6824 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6825 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6826 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6827 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6829 vtl->route_finder_added_track = NULL;
6830 vtl->route_finder_check_added_track = FALSE;
6831 vtl->route_finder_append = FALSE;
6833 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6835 vtl->route_finder_started = TRUE;
6836 vtl->route_finder_coord = tmp;
6837 vtl->route_finder_current_track = NULL;
6843 /*** Show picture ****/
6845 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6850 /* Params are: vvp, event, last match found or NULL */
6851 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6853 if ( wp->image && wp->visible )
6855 gint x, y, slackx, slacky;
6856 GdkEventButton *event = (GdkEventButton *) params[1];
6858 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6859 slackx = wp->image_width / 2;
6860 slacky = wp->image_height / 2;
6861 if ( x <= event->x + slackx && x >= event->x - slackx
6862 && y <= event->y + slacky && y >= event->y - slacky )
6864 params[2] = wp->image; /* we've found a match. however continue searching
6865 * since we want to find the last match -- that
6866 * is, the match that was drawn last. */
6871 static void trw_layer_show_picture ( gpointer pass_along[6] )
6873 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6875 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
6878 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6879 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6880 g_free ( quoted_file );
6881 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6883 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() );
6884 g_error_free ( err );
6887 #endif /* WINDOWS */
6890 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6892 gpointer params[3] = { vvp, event, NULL };
6893 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6895 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6898 static gpointer pass_along[6];
6899 pass_along[0] = vtl;
6900 pass_along[5] = params[2];
6901 trw_layer_show_picture ( pass_along );
6902 return TRUE; /* found a match */
6905 return FALSE; /* go through other layers, searching for a match */
6908 /***************************************************************************
6910 ***************************************************************************/
6916 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6918 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6919 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6922 /* Structure for thumbnail creating data used in the background thread */
6924 VikTrwLayer *vtl; // Layer needed for redrawing
6925 GSList *pics; // Image list
6926 } thumbnail_create_thread_data;
6928 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6930 guint total = g_slist_length(tctd->pics), done = 0;
6931 while ( tctd->pics )
6933 a_thumbnails_create ( (gchar *) tctd->pics->data );
6934 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6936 return -1; /* Abort thread */
6938 tctd->pics = tctd->pics->next;
6941 // Redraw to show the thumbnails as they are now created
6942 if ( IS_VIK_LAYER(tctd->vtl) )
6943 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6948 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6950 while ( tctd->pics )
6952 g_free ( tctd->pics->data );
6953 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6958 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6960 if ( ! vtl->has_verified_thumbnails )
6962 GSList *pics = NULL;
6963 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6966 gint len = g_slist_length ( pics );
6967 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6968 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6971 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6973 (vik_thr_func) create_thumbnails_thread,
6975 (vik_thr_free_func) thumbnail_create_thread_free,
6983 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6985 return vtl->coord_mode;
6989 * Uniquify the whole layer
6990 * Also requires the layers panel as the names shown there need updating too
6991 * Returns whether the operation was successful or not
6993 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6996 vik_trw_layer_uniquify_tracks ( vtl, vlp );
6997 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
7003 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
7005 vik_coord_convert ( &(wp->coord), *dest_mode );
7008 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
7010 vik_track_convert ( tr, *dest_mode );
7013 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
7015 if ( vtl->coord_mode != dest_mode )
7017 vtl->coord_mode = dest_mode;
7018 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
7019 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
7023 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
7025 vtl->menu_selection = selection;
7028 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
7030 return (vtl->menu_selection);
7033 /* ----------- Downloading maps along tracks --------------- */
7035 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
7037 /* TODO: calculating based on current size of viewport */
7038 const gdouble w_at_zoom_0_125 = 0.0013;
7039 const gdouble h_at_zoom_0_125 = 0.0011;
7040 gdouble zoom_factor = zoom_level/0.125;
7042 wh->lat = h_at_zoom_0_125 * zoom_factor;
7043 wh->lon = w_at_zoom_0_125 * zoom_factor;
7045 return 0; /* all OK */
7048 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
7050 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
7051 (dist->lat >= ABS(to->north_south - from->north_south)))
7054 VikCoord *coord = g_malloc(sizeof(VikCoord));
7055 coord->mode = VIK_COORD_LATLON;
7057 if (ABS(gradient) < 1) {
7058 if (from->east_west > to->east_west)
7059 coord->east_west = from->east_west - dist->lon;
7061 coord->east_west = from->east_west + dist->lon;
7062 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
7064 if (from->north_south > to->north_south)
7065 coord->north_south = from->north_south - dist->lat;
7067 coord->north_south = from->north_south + dist->lat;
7068 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
7074 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
7076 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
7077 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
7079 VikCoord *next = from;
7081 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
7083 list = g_list_prepend(list, next);
7089 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
7091 typedef struct _Rect {
7096 #define GLRECT(iter) ((Rect *)((iter)->data))
7099 GList *rects_to_download = NULL;
7102 if (get_download_area_width(vvp, zoom_level, &wh))
7105 GList *iter = tr->trackpoints;
7109 gboolean new_map = TRUE;
7110 VikCoord *cur_coord, tl, br;
7113 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
7115 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7116 rect = g_malloc(sizeof(Rect));
7119 rect->center = *cur_coord;
7120 rects_to_download = g_list_prepend(rects_to_download, rect);
7125 gboolean found = FALSE;
7126 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7127 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7138 GList *fillins = NULL;
7139 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
7140 /* seems that ATM the function get_next_coord works only for LATLON */
7141 if ( cur_coord->mode == VIK_COORD_LATLON ) {
7142 /* fill-ins for far apart points */
7143 GList *cur_rect, *next_rect;
7144 for (cur_rect = rects_to_download;
7145 (next_rect = cur_rect->next) != NULL;
7146 cur_rect = cur_rect->next) {
7147 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
7148 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
7149 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
7153 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
7156 GList *iter = fillins;
7158 cur_coord = (VikCoord *)(iter->data);
7159 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7160 rect = g_malloc(sizeof(Rect));
7163 rect->center = *cur_coord;
7164 rects_to_download = g_list_prepend(rects_to_download, rect);
7169 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7170 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7174 for (iter = fillins; iter; iter = iter->next)
7176 g_list_free(fillins);
7178 if (rects_to_download) {
7179 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
7180 g_free(rect_iter->data);
7181 g_list_free(rects_to_download);
7185 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7188 gint selected_map, default_map;
7189 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
7190 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
7191 gint selected_zoom, default_zoom;
7195 VikTrwLayer *vtl = pass_along[0];
7196 VikLayersPanel *vlp = pass_along[1];
7197 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7198 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
7200 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7201 int num_maps = g_list_length(vmls);
7204 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7208 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
7209 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
7211 gchar **np = map_names;
7212 VikMapsLayer **lp = map_layers;
7213 for (i = 0; i < num_maps; i++) {
7214 gboolean dup = FALSE;
7215 vml = (VikMapsLayer *)(vmls->data);
7216 for (j = 0; j < i; j++) { /* no duplicate allowed */
7217 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
7224 *np++ = vik_maps_layer_get_map_label(vml);
7230 num_maps = lp - map_layers;
7232 for (default_map = 0; default_map < num_maps; default_map++) {
7233 /* TODO: check for parent layer's visibility */
7234 if (VIK_LAYER(map_layers[default_map])->visible)
7237 default_map = (default_map == num_maps) ? 0 : default_map;
7239 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
7240 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
7241 if (cur_zoom == zoom_vals[default_zoom])
7244 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
7246 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
7249 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7252 for (i = 0; i < num_maps; i++)
7253 g_free(map_names[i]);
7261 /**** lowest waypoint number calculation ***/
7262 static gint highest_wp_number_name_to_number(const gchar *name) {
7263 if ( strlen(name) == 3 ) {
7265 if ( n < 100 && name[0] != '0' )
7267 if ( n < 10 && name[0] != '0' )
7275 static void highest_wp_number_reset(VikTrwLayer *vtl)
7277 vtl->highest_wp_number = -1;
7280 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
7282 /* if is bigger that top, add it */
7283 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
7284 if ( new_wp_num > vtl->highest_wp_number )
7285 vtl->highest_wp_number = new_wp_num;
7288 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
7290 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
7291 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
7292 if ( vtl->highest_wp_number == old_wp_num ) {
7294 vtl->highest_wp_number--;
7296 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7297 /* search down until we find something that *does* exist */
7299 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
7300 vtl->highest_wp_number--;
7301 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7306 /* get lowest unused number */
7307 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
7310 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
7312 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
7313 return g_strdup(buf);