2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
26 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
33 #include "vikmapslayer.h"
34 #include "vikgpslayer.h"
35 #include "viktrwlayer_tpwin.h"
36 #include "viktrwlayer_propwin.h"
37 #ifdef VIK_CONFIG_GEOTAG
38 #include "viktrwlayer_geotag.h"
39 #include "geotag_exif.h"
41 #include "garminsymbols.h"
42 #include "thumbnails.h"
43 #include "background.h"
48 #include "geonamessearch.h"
49 #ifdef VIK_CONFIG_OPENSTREETMAP
50 #include "osm-traces.h"
53 #include "datasources.h"
54 #include "datasource_gps.h"
57 #include "icons/icons.h"
71 #include <gdk/gdkkeysyms.h>
73 #include <glib/gstdio.h>
74 #include <glib/gi18n.h>
76 #ifdef VIK_CONFIG_GOOGLE
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
80 #define VIK_TRW_LAYER_TRACK_GC 5
81 #define VIK_TRW_LAYER_TRACK_GCS 10
82 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
83 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
84 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
85 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
86 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
88 #define DRAWMODE_BY_TRACK 0
89 #define DRAWMODE_BY_SPEED 1
90 #define DRAWMODE_ALL_BLACK 2
91 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
92 // as we are (re)calculating the colour for every point
97 /* this is how it knows when you click if you are clicking close to a trackpoint. */
98 #define TRACKPOINT_SIZE_APPROX 5
99 #define WAYPOINT_SIZE_APPROX 5
101 #define MIN_STOP_LENGTH 15
102 #define MAX_STOP_LENGTH 86400
103 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
104 /* this is multiplied by user-inputted value from 1-100. */
106 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
108 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
110 FS_XX_SMALL = 0, // 'xx-small'
113 FS_MEDIUM, // DEFAULT
120 struct _VikTrwLayer {
123 GHashTable *tracks_iters;
125 GHashTable *routes_iters;
126 GHashTable *waypoints_iters;
127 GHashTable *waypoints;
128 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
129 gboolean tracks_visible, routes_visible, waypoints_visible;
132 guint8 drawpoints_size;
133 guint8 drawelevation;
134 guint8 elevation_factor;
138 guint8 drawdirections;
139 guint8 drawdirections_size;
140 guint8 line_thickness;
141 guint8 bg_line_thickness;
145 gboolean wp_draw_symbols;
146 font_size_t wp_font_size;
148 gdouble track_draw_speed_factor;
150 GdkGC *track_1color_gc;
151 GdkGC *current_track_gc;
152 // Separate GC for a track's potential new point as drawn via separate method
153 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
154 GdkGC *current_track_newpoint_gc;
157 GdkGC *waypoint_text_gc;
158 GdkGC *waypoint_bg_gc;
159 GdkFont *waypoint_font;
160 VikTrack *current_track; // ATM shared between new tracks and new routes
161 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
162 gboolean draw_sync_done;
163 gboolean draw_sync_do;
165 VikCoordMode coord_mode;
167 /* wp editing tool */
168 VikWaypoint *current_wp;
169 gpointer current_wp_id;
171 gboolean waypoint_rightclick;
173 /* track editing tool */
175 VikTrack *current_tp_track;
176 gpointer current_tp_id;
177 VikTrwLayerTpwin *tpwin;
179 /* track editing tool -- more specifically, moving tps */
182 /* route finder tool */
183 gboolean route_finder_started;
184 VikCoord route_finder_coord;
185 gboolean route_finder_check_added_track;
186 VikTrack *route_finder_added_track;
187 VikTrack *route_finder_current_track;
188 gboolean route_finder_append;
195 guint16 image_cache_size;
197 /* for waypoint text */
198 PangoLayout *wplabellayout;
200 gboolean has_verified_thumbnails;
202 GtkMenu *wp_right_click_menu;
203 GtkMenu *track_right_click_menu;
206 VikStdLayerMenuItem menu_selection;
208 gint highest_wp_number;
211 /* A caached waypoint image. */
214 gchar *image; /* filename */
217 struct DrawingParams {
222 guint16 width, height;
223 gdouble cc; // Cosine factor in track directions
224 gdouble ss; // Sine factor in track directions
225 const VikCoord *center;
226 gboolean one_zone, lat_lon;
227 gdouble ce1, ce2, cn1, cn2;
230 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
232 static void trw_layer_delete_item ( gpointer pass_along[6] );
233 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
234 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
236 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] );
237 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
238 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
240 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
241 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
243 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
244 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
246 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
247 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
248 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
249 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
250 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
251 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
252 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
253 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
254 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
255 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
256 static void trw_layer_append_track ( gpointer pass_along[6] );
257 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
258 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
259 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
260 static void trw_layer_split_segments ( gpointer pass_along[6] );
261 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
262 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
263 static void trw_layer_reverse ( gpointer pass_along[6] );
264 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
265 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
266 static void trw_layer_show_picture ( gpointer pass_along[6] );
267 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
269 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
270 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
271 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
272 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
273 static void trw_layer_new_wp ( gpointer lav[2] );
274 static void trw_layer_new_track ( gpointer lav[2] );
275 static void trw_layer_new_route ( gpointer lav[2] );
276 static void trw_layer_finish_track ( gpointer lav[2] );
277 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
278 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
279 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
280 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
281 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
282 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
283 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
284 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
285 #ifdef VIK_CONFIG_GEOTAG
286 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
287 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
288 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
289 static void trw_layer_geotagging ( gpointer lav[2] );
291 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
292 #ifdef VIK_CONFIG_GOOGLE
293 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
295 #ifdef VIK_CONFIG_OPENSTREETMAP
296 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
298 #ifdef VIK_CONFIG_GEOCACHES
299 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
301 #ifdef VIK_CONFIG_GEOTAG
302 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
304 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
305 static void trw_layer_gps_upload ( gpointer lav[2] );
307 // Specific route versions:
308 // Most track handling functions can handle operating on the route list
309 // However these ones are easier in separate functions
310 static void trw_layer_auto_routes_view ( gpointer lav[2] );
311 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
314 static void trw_layer_properties_item ( gpointer pass_along[7] );
315 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
316 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
317 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
319 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
320 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
321 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
323 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
324 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
325 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
326 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
328 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
329 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
330 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
331 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
332 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
333 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
334 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
335 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
336 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
337 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
338 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
339 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
340 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
341 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
342 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
343 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
344 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
345 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
346 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
347 #ifdef VIK_CONFIG_GOOGLE
348 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
349 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
352 static void cached_pixbuf_free ( CachedPixbuf *cp );
353 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
355 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
356 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
358 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
359 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
361 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
362 static void highest_wp_number_reset(VikTrwLayer *vtl);
363 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
364 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
366 // Note for the following tool GtkRadioActionEntry texts:
367 // the very first text value is an internal name not displayed anywhere
368 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
369 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
370 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
371 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
372 static VikToolInterface trw_layer_tools[] = {
373 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
374 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
375 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
377 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
379 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
380 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
381 (VikToolMouseFunc) tool_new_track_click,
382 (VikToolMouseMoveFunc) tool_new_track_move,
383 (VikToolMouseFunc) tool_new_track_release,
384 (VikToolKeyFunc) tool_new_track_key_press,
385 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
386 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
388 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
389 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
390 (VikToolMouseFunc) tool_new_route_click,
391 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
392 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
393 (VikToolKeyFunc) tool_new_track_key_press, // -/#
394 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
395 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
397 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
398 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
399 (VikToolMouseFunc) tool_edit_waypoint_click,
400 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
401 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
403 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
405 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
406 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
407 (VikToolMouseFunc) tool_edit_trackpoint_click,
408 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
409 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
411 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
413 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
414 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
415 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
417 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
419 #ifdef VIK_CONFIG_GOOGLE
420 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
421 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
422 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
424 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
429 TOOL_CREATE_WAYPOINT=0,
433 TOOL_EDIT_TRACKPOINT,
435 #ifdef VIK_CONFIG_GOOGLE
441 /****** PARAMETERS ******/
443 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
444 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
446 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
447 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
449 #define MIN_POINT_SIZE 2
450 #define MAX_POINT_SIZE 10
452 #define MIN_ARROW_SIZE 3
453 #define MAX_ARROW_SIZE 20
455 static VikLayerParamScale params_scales[] = {
456 /* min max step digits */
457 { 1, 10, 1, 0 }, /* line_thickness */
458 { 0, 100, 1, 0 }, /* track draw speed factor */
459 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
460 /* 5 * step == how much to turn */
461 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
462 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
463 { 5, 500, 5, 0 }, // 5: image cache_size - " "
464 { 0, 8, 1, 0 }, // 6: Background line thickness
465 { 1, 64, 1, 0 }, /* wpsize */
466 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
467 { 1, 100, 1, 0 }, // 9: elevation factor
468 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
469 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
472 static gchar* params_font_sizes[] = {
473 N_("Extra Extra Small"),
479 N_("Extra Extra Large"),
482 VikLayerParam trw_layer_params[] = {
483 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
484 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
485 { "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
487 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL },
488 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
489 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[0], NULL, NULL },
490 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
491 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL },
492 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
493 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL },
494 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
495 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[9], NULL, NULL },
497 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
498 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time") },
499 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[8], NULL, NULL },
501 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[6], NULL, NULL},
502 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
503 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
504 N_("The percentage factor away from the average speed determining the color used") },
506 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
507 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL },
508 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
509 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
510 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
511 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
512 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL },
513 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[7], NULL, NULL },
514 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
516 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
517 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[3], NULL, NULL },
518 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[4], NULL, NULL },
519 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[5], NULL, NULL },
522 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
524 // Sublayer visibilities
562 *** 1) Add to trw_layer_params and enumeration
563 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
566 /****** END PARAMETERS ******/
568 static VikTrwLayer* trw_layer_new ( gint drawmode );
569 /* Layer Interface function definitions */
570 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
571 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
572 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp );
573 static void trw_layer_free ( VikTrwLayer *trwlayer );
574 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
575 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
576 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
577 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
578 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
579 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
580 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
581 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
582 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
583 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
584 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
585 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
586 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
587 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
588 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
589 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
590 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
591 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
592 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
593 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
594 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
595 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
596 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
597 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
598 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
599 /* End Layer Interface function definitions */
601 VikLayerInterface vik_trw_layer_interface = {
608 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
612 params_groups, /* params_groups */
613 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
617 (VikLayerFuncCreate) trw_layer_create,
618 (VikLayerFuncRealize) trw_layer_realize,
619 (VikLayerFuncPostRead) trw_layer_post_read,
620 (VikLayerFuncFree) trw_layer_free,
622 (VikLayerFuncProperties) NULL,
623 (VikLayerFuncDraw) trw_layer_draw,
624 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
626 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
627 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
629 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
630 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
632 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
633 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
634 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
635 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
636 (VikLayerFuncLayerSelected) trw_layer_selected,
638 (VikLayerFuncMarshall) trw_layer_marshall,
639 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
641 (VikLayerFuncSetParam) trw_layer_set_param,
642 (VikLayerFuncGetParam) trw_layer_get_param,
644 (VikLayerFuncReadFileData) a_gpspoint_read_file,
645 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
647 (VikLayerFuncDeleteItem) trw_layer_del_item,
648 (VikLayerFuncCutItem) trw_layer_cut_item,
649 (VikLayerFuncCopyItem) trw_layer_copy_item,
650 (VikLayerFuncPasteItem) trw_layer_paste_item,
651 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
653 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
655 (VikLayerFuncSelectClick) trw_layer_select_click,
656 (VikLayerFuncSelectMove) trw_layer_select_move,
657 (VikLayerFuncSelectRelease) trw_layer_select_release,
658 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
661 GType vik_trw_layer_get_type ()
663 static GType vtl_type = 0;
667 static const GTypeInfo vtl_info =
669 sizeof (VikTrwLayerClass),
670 NULL, /* base_init */
671 NULL, /* base_finalize */
672 NULL, /* class init */
673 NULL, /* class_finalize */
674 NULL, /* class_data */
675 sizeof (VikTrwLayer),
677 NULL /* instance init */
679 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
685 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
687 static gpointer pass_along[6];
693 pass_along[1] = NULL;
694 pass_along[2] = GINT_TO_POINTER (subtype);
695 pass_along[3] = sublayer;
696 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
697 pass_along[5] = NULL;
699 trw_layer_delete_item ( pass_along );
702 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
704 static gpointer pass_along[6];
710 pass_along[1] = NULL;
711 pass_along[2] = GINT_TO_POINTER (subtype);
712 pass_along[3] = sublayer;
713 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
714 pass_along[5] = NULL;
716 trw_layer_copy_item_cb(pass_along);
717 trw_layer_cut_item_cb(pass_along);
720 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
722 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
723 gint subtype = GPOINTER_TO_INT (pass_along[2]);
724 gpointer * sublayer = pass_along[3];
728 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
732 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
733 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
734 if ( wp && wp->name )
737 name = NULL; // Broken :(
739 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
740 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
741 if ( trk && trk->name )
744 name = NULL; // Broken :(
747 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
748 if ( trk && trk->name )
751 name = NULL; // Broken :(
754 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
755 subtype, len, name, data);
759 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
761 trw_layer_copy_item_cb(pass_along);
762 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
763 trw_layer_delete_item(pass_along);
766 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
768 // Slightly cheating method, routing via the panels capability
769 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
772 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
782 GByteArray *ba = g_byte_array_new ();
784 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
785 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
786 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
787 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
789 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
792 g_byte_array_append ( ba, id, il );
800 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
807 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
811 w = vik_waypoint_unmarshall ( item, len );
812 // When copying - we'll create a new name based on the original
813 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
814 vik_trw_layer_add_waypoint ( vtl, name, w );
815 waypoint_convert (NULL, w, &vtl->coord_mode);
817 // Consider if redraw necessary for the new item
818 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
819 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
822 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
826 t = vik_track_unmarshall ( item, len );
827 // When copying - we'll create a new name based on the original
828 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
829 vik_trw_layer_add_track ( vtl, name, t );
830 vik_track_convert (t, vtl->coord_mode);
832 // Consider if redraw necessary for the new item
833 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
834 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
837 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
841 t = vik_track_unmarshall ( item, len );
842 // When copying - we'll create a new name based on the original
843 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
844 vik_trw_layer_add_route ( vtl, name, t );
845 vik_track_convert (t, vtl->coord_mode);
847 // Consider if redraw necessary for the new item
848 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
849 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
855 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
862 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
866 case PARAM_TV: vtl->tracks_visible = data.b; break;
867 case PARAM_WV: vtl->waypoints_visible = data.b; break;
868 case PARAM_RV: vtl->routes_visible = data.b; break;
869 case PARAM_DM: vtl->drawmode = data.u; break;
870 case PARAM_DP: vtl->drawpoints = data.b; break;
872 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
873 vtl->drawpoints_size = data.u;
875 case PARAM_DE: vtl->drawelevation = data.b; break;
876 case PARAM_DS: vtl->drawstops = data.b; break;
877 case PARAM_DL: vtl->drawlines = data.b; break;
878 case PARAM_DD: vtl->drawdirections = data.b; break;
880 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
881 vtl->drawdirections_size = data.u;
883 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
884 vtl->stop_length = data.u;
886 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
887 vtl->elevation_factor = data.u;
889 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
891 vtl->line_thickness = data.u;
892 trw_layer_new_track_gcs ( vtl, vp );
895 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
897 vtl->bg_line_thickness = data.u;
898 trw_layer_new_track_gcs ( vtl, vp );
901 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
902 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
903 case PARAM_DLA: vtl->drawlabels = data.b; break;
904 case PARAM_DI: vtl->drawimages = data.b; break;
905 case PARAM_IS: if ( data.u != vtl->image_size )
907 vtl->image_size = data.u;
908 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
909 g_queue_free ( vtl->image_cache );
910 vtl->image_cache = g_queue_new ();
913 case PARAM_IA: vtl->image_alpha = data.u; break;
914 case PARAM_ICS: vtl->image_cache_size = data.u;
915 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
916 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
918 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
919 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
920 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
921 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
922 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
923 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
924 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
925 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
930 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
932 VikLayerParamData rv;
935 case PARAM_TV: rv.b = vtl->tracks_visible; break;
936 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
937 case PARAM_RV: rv.b = vtl->routes_visible; break;
938 case PARAM_DM: rv.u = vtl->drawmode; break;
939 case PARAM_DP: rv.b = vtl->drawpoints; break;
940 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
941 case PARAM_DE: rv.b = vtl->drawelevation; break;
942 case PARAM_EF: rv.u = vtl->elevation_factor; break;
943 case PARAM_DS: rv.b = vtl->drawstops; break;
944 case PARAM_SL: rv.u = vtl->stop_length; break;
945 case PARAM_DL: rv.b = vtl->drawlines; break;
946 case PARAM_DD: rv.b = vtl->drawdirections; break;
947 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
948 case PARAM_LT: rv.u = vtl->line_thickness; break;
949 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
950 case PARAM_DLA: rv.b = vtl->drawlabels; break;
951 case PARAM_DI: rv.b = vtl->drawimages; break;
952 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
953 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
954 case PARAM_IS: rv.u = vtl->image_size; break;
955 case PARAM_IA: rv.u = vtl->image_alpha; break;
956 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
957 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
958 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
959 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
960 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
961 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
962 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
963 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
964 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
969 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
976 // Use byte arrays to store sublayer data
977 // much like done elsewhere e.g. vik_layer_marshall_params()
978 GByteArray *ba = g_byte_array_new ( );
986 // the length of the item
987 // the sublayer type of item
988 // the the actual item
989 #define tlm_append(object_pointer, size, type) \
991 object_length = (size); \
992 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
993 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
994 g_byte_array_append ( ba, (object_pointer), object_length );
996 // Layer parameters first
997 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
998 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
999 g_byte_array_append ( ba, pd, pl );
1002 // Now sublayer data
1003 GHashTableIter iter;
1004 gpointer key, value;
1007 g_hash_table_iter_init ( &iter, vtl->waypoints );
1008 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1009 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1010 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1015 g_hash_table_iter_init ( &iter, vtl->tracks );
1016 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1017 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1018 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1023 g_hash_table_iter_init ( &iter, vtl->routes );
1024 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1025 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1026 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1036 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1038 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1040 gint consumed_length;
1042 // First the overall layer parameters
1043 memcpy(&pl, data, sizeof(pl));
1045 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1048 consumed_length = pl;
1049 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1051 #define tlm_size (*(gint *)data)
1052 // See marshalling above for order of how this is written
1054 data += sizeof_len_and_subtype + tlm_size;
1056 // Now the individual sublayers:
1058 while ( *data && consumed_length < len ) {
1059 // Normally four extra bytes at the end of the datastream
1060 // (since it's a GByteArray and that's where it's length is stored)
1061 // So only attempt read when there's an actual block of sublayer data
1062 if ( consumed_length + tlm_size < len ) {
1064 // Reuse pl to read the subtype from the data stream
1065 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1067 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1068 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1069 gchar *name = g_strdup ( trk->name );
1070 vik_trw_layer_add_track ( vtl, name, trk );
1073 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1074 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1075 gchar *name = g_strdup ( wp->name );
1076 vik_trw_layer_add_waypoint ( vtl, name, wp );
1079 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1080 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1081 gchar *name = g_strdup ( trk->name );
1082 vik_trw_layer_add_route ( vtl, name, trk );
1086 consumed_length += tlm_size + sizeof_len_and_subtype;
1089 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1094 // Keep interesting hash function at least visible
1096 static guint strcase_hash(gconstpointer v)
1098 // 31 bit hash function
1101 gchar s[128]; // malloc is too slow for reading big files
1104 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1105 p[i] = toupper(t[i]);
1111 for (p += 1; *p != '\0'; p++)
1112 h = (h << 5) - h + *p;
1119 static VikTrwLayer* trw_layer_new ( gint drawmode )
1121 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1122 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1124 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1125 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1127 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1128 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1129 // and with normal PC processing capabilities - it has negligibile performance impact
1130 // This also minimized the amount of rework - as the management of the hash tables already exists.
1132 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1133 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1134 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1136 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1137 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1138 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1139 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1140 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1141 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1144 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1145 rv->drawmode = drawmode;
1146 rv->drawpoints = TRUE;
1147 rv->drawpoints_size = MIN_POINT_SIZE;
1148 rv->drawdirections_size = 5;
1149 rv->elevation_factor = 30;
1150 rv->stop_length = 60;
1151 rv->drawlines = TRUE;
1152 rv->track_draw_speed_factor = 30.0;
1153 rv->line_thickness = 1;
1155 rv->draw_sync_done = TRUE;
1156 rv->draw_sync_do = TRUE;
1158 rv->image_cache = g_queue_new();
1159 rv->image_size = 64;
1160 rv->image_alpha = 255;
1161 rv->image_cache_size = 300;
1162 rv->drawimages = TRUE;
1163 rv->drawlabels = TRUE;
1164 // Everything else is 0, FALSE or NULL
1170 static void trw_layer_free ( VikTrwLayer *trwlayer )
1172 g_hash_table_destroy(trwlayer->waypoints);
1173 g_hash_table_destroy(trwlayer->tracks);
1175 /* ODC: replace with GArray */
1176 trw_layer_free_track_gcs ( trwlayer );
1178 if ( trwlayer->wp_right_click_menu )
1179 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1181 if ( trwlayer->track_right_click_menu )
1182 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1184 if ( trwlayer->wplabellayout != NULL)
1185 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1187 if ( trwlayer->waypoint_gc != NULL )
1188 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1190 if ( trwlayer->waypoint_text_gc != NULL )
1191 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1193 if ( trwlayer->waypoint_bg_gc != NULL )
1194 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1196 if ( trwlayer->tpwin != NULL )
1197 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1199 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1200 g_queue_free ( trwlayer->image_cache );
1203 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1207 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1208 dp->xmpp = vik_viewport_get_xmpp ( vp );
1209 dp->ympp = vik_viewport_get_ympp ( vp );
1210 dp->width = vik_viewport_get_width ( vp );
1211 dp->height = vik_viewport_get_height ( vp );
1212 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1213 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1215 dp->center = vik_viewport_get_center ( vp );
1216 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1217 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1222 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1223 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1224 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1226 dp->ce1 = dp->center->east_west-w2;
1227 dp->ce2 = dp->center->east_west+w2;
1228 dp->cn1 = dp->center->north_south-h2;
1229 dp->cn2 = dp->center->north_south+h2;
1230 } else if ( dp->lat_lon ) {
1231 VikCoord upperleft, bottomright;
1232 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1233 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1234 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1235 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1236 dp->ce1 = upperleft.east_west;
1237 dp->ce2 = bottomright.east_west;
1238 dp->cn1 = bottomright.north_south;
1239 dp->cn2 = upperleft.north_south;
1244 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1245 * Here a simple traffic like light colour system is used:
1246 * . slow points are red
1247 * . average is yellow
1248 * . fast points are green
1250 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1253 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1254 if ( average_speed > 0 ) {
1255 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1256 if ( rv < low_speed )
1257 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1258 else if ( rv > high_speed )
1259 return VIK_TRW_LAYER_TRACK_GC_FAST;
1261 return VIK_TRW_LAYER_TRACK_GC_AVER;
1264 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1267 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1269 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1270 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1271 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1272 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1275 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1277 /* TODO: this function is a mess, get rid of any redundancy */
1278 GList *list = track->trackpoints;
1280 gboolean useoldvals = TRUE;
1282 gboolean drawpoints;
1284 gboolean drawelevation;
1285 gdouble min_alt, max_alt, alt_diff = 0;
1287 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1288 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1291 if ( dp->vtl->drawelevation )
1293 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1294 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1295 alt_diff = max_alt - min_alt;
1298 if ( ! track->visible )
1301 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1302 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1303 trw_layer_draw_track ( id, track, dp, TRUE );
1305 if ( draw_track_outline )
1306 drawpoints = drawstops = FALSE;
1308 drawpoints = dp->vtl->drawpoints;
1309 drawstops = dp->vtl->drawstops;
1312 gboolean drawing_highlight = FALSE;
1313 /* Current track - used for creation */
1314 if ( track == dp->vtl->current_track )
1315 main_gc = dp->vtl->current_track_gc;
1317 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1318 /* Draw all tracks of the layer in special colour */
1319 /* if track is member of selected layer or is the current selected track
1320 then draw in the highlight colour.
1321 NB this supercedes the drawmode */
1322 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1323 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1324 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1325 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1326 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1327 drawing_highlight = TRUE;
1330 if ( !drawing_highlight ) {
1331 // Still need to figure out the gc according to the drawing mode:
1332 switch ( dp->vtl->drawmode ) {
1333 case DRAWMODE_BY_TRACK:
1334 if ( dp->vtl->track_1color_gc )
1335 g_object_unref ( dp->vtl->track_1color_gc );
1336 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1337 main_gc = dp->vtl->track_1color_gc;
1340 // Mostly for DRAWMODE_ALL_BLACK
1341 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1342 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_BLACK);
1349 int x, y, oldx, oldy;
1350 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1352 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1354 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1356 // Draw the first point as something a bit different from the normal points
1357 // ATM it's slightly bigger and a triangle
1359 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1360 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1366 gdouble average_speed = 0.0;
1367 gdouble low_speed = 0.0;
1368 gdouble high_speed = 0.0;
1369 // If necessary calculate these values - which is done only once per track redraw
1370 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1371 // the percentage factor away from the average speed determines transistions between the levels
1372 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1373 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1374 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1377 while ((list = g_list_next(list)))
1379 tp = VIK_TRACKPOINT(list->data);
1380 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1382 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1383 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1384 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1385 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1386 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1388 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1391 * If points are the same in display coordinates, don't draw.
1393 if ( useoldvals && x == oldx && y == oldy )
1395 // Still need to process points to ensure 'stops' are drawn if required
1396 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1397 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1398 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 );
1403 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1404 if ( drawpoints || dp->vtl->drawlines ) {
1405 // setup main_gc for both point and line drawing
1406 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1407 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
1411 if ( drawpoints && ! draw_track_outline )
1416 * The concept of drawing stops is that a trackpoint
1417 * that is if the next trackpoint has a timestamp far into
1418 * the future, we draw a circle of 6x trackpoint size,
1419 * instead of a rectangle of 2x trackpoint size.
1420 * This is drawn first so the trackpoint will be drawn on top
1423 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1424 /* Stop point. Draw 6x circle. Always in redish colour */
1425 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
1427 /* Regular point - draw 2x square. */
1428 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1431 /* Final point - draw 4x circle. */
1432 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 );
1435 if ((!tp->newsegment) && (dp->vtl->drawlines))
1438 /* UTM only: zone check */
1439 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1440 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1443 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1445 if ( draw_track_outline ) {
1446 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1450 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1452 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1454 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1459 tmp[1].y = oldy-FIXALTITUDE(list->data);
1461 tmp[2].y = y-FIXALTITUDE(list->next->data);
1466 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1467 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1469 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1470 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1472 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1477 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1478 // Draw an arrow at the mid point to show the direction of the track
1479 // Code is a rework from vikwindow::draw_ruler()
1480 gint midx = (oldx + x) / 2;
1481 gint midy = (oldy + y) / 2;
1483 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1484 // Avoid divide by zero and ensure at least 1 pixel big
1486 gdouble dx = (oldx - midx) / len;
1487 gdouble dy = (oldy - midy) / len;
1488 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1489 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1499 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1501 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1502 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1504 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1506 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1507 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
1511 * If points are the same in display coordinates, don't draw.
1513 if ( x != oldx || y != oldy )
1515 if ( draw_track_outline )
1516 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1518 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1524 * If points are the same in display coordinates, don't draw.
1526 if ( x != oldx && y != oldy )
1528 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1529 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1539 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1540 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1542 trw_layer_draw_track ( id, track, dp, FALSE );
1545 static void cached_pixbuf_free ( CachedPixbuf *cp )
1547 g_object_unref ( G_OBJECT(cp->pixbuf) );
1548 g_free ( cp->image );
1551 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1553 return strcmp ( cp->image, name );
1556 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1559 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1560 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1561 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1564 GdkPixbuf *sym = NULL;
1565 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1567 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1569 if ( wp->image && dp->vtl->drawimages )
1571 GdkPixbuf *pixbuf = NULL;
1574 if ( dp->vtl->image_alpha == 0)
1577 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1579 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1582 gchar *image = wp->image;
1583 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1584 if ( ! regularthumb )
1586 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1587 image = "\x12\x00"; /* this shouldn't occur naturally. */
1591 CachedPixbuf *cp = NULL;
1592 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1593 if ( dp->vtl->image_size == 128 )
1594 cp->pixbuf = regularthumb;
1597 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1598 g_assert ( cp->pixbuf );
1599 g_object_unref ( G_OBJECT(regularthumb) );
1601 cp->image = g_strdup ( image );
1603 /* needed so 'click picture' tool knows how big the pic is; we don't
1604 * store it in cp because they may have been freed already. */
1605 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1606 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1608 g_queue_push_head ( dp->vtl->image_cache, cp );
1609 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1610 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1612 pixbuf = cp->pixbuf;
1616 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1622 w = gdk_pixbuf_get_width ( pixbuf );
1623 h = gdk_pixbuf_get_height ( pixbuf );
1625 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1627 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1628 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1629 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1630 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
1631 // Highlighted - so draw a little border around the chosen one
1632 // single line seems a little weak so draw 2 of them
1633 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1634 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1635 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1636 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1639 if ( dp->vtl->image_alpha == 255 )
1640 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1642 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1644 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1648 /* DRAW ACTUAL DOT */
1649 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1650 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 );
1652 else if ( wp == dp->vtl->current_wp ) {
1653 switch ( dp->vtl->wp_symbol ) {
1654 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;
1655 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;
1656 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;
1657 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 );
1658 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 );
1662 switch ( dp->vtl->wp_symbol ) {
1663 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;
1664 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;
1665 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;
1666 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 );
1667 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;
1671 if ( dp->vtl->drawlabels )
1673 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1674 gint label_x, label_y;
1676 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1678 // Could this stored in the waypoint rather than recreating each pass?
1679 gchar *fsize = NULL;
1680 switch (dp->vtl->wp_font_size) {
1681 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1682 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1683 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1684 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1685 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1686 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1687 default: fsize = g_strdup ( "medium" ); break;
1690 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1692 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1693 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1695 // Fallback if parse failure
1696 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1698 g_free ( wp_label_markup );
1701 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1702 label_x = x - width/2;
1704 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1706 label_y = y - dp->vtl->wp_size - height - 2;
1708 /* if highlight mode on, then draw background text in highlight colour */
1709 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1710 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1711 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1712 wp == vik_window_get_selected_waypoint ( dp->vw ) )
1713 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1715 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1718 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1720 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1725 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1727 static struct DrawingParams dp;
1728 g_assert ( l != NULL );
1730 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1732 if ( l->tracks_visible )
1733 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1735 if ( l->routes_visible )
1736 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1738 if (l->waypoints_visible)
1739 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1742 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1745 if ( vtl->track_bg_gc )
1747 g_object_unref ( vtl->track_bg_gc );
1748 vtl->track_bg_gc = NULL;
1750 if ( vtl->track_1color_gc )
1752 g_object_unref ( vtl->track_1color_gc );
1753 vtl->track_1color_gc = NULL;
1755 if ( vtl->current_track_gc )
1757 g_object_unref ( vtl->current_track_gc );
1758 vtl->current_track_gc = NULL;
1760 if ( vtl->current_track_newpoint_gc )
1762 g_object_unref ( vtl->current_track_newpoint_gc );
1763 vtl->current_track_newpoint_gc = NULL;
1766 if ( ! vtl->track_gc )
1768 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1769 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1770 g_array_free ( vtl->track_gc, TRUE );
1771 vtl->track_gc = NULL;
1774 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1776 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1777 gint width = vtl->line_thickness;
1779 if ( vtl->track_gc )
1780 trw_layer_free_track_gcs ( vtl );
1782 if ( vtl->track_bg_gc )
1783 g_object_unref ( vtl->track_bg_gc );
1784 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1786 // Ensure new track drawing heeds line thickness setting
1787 // however always have a minium of 2, as 1 pixel is really narrow
1788 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1790 if ( vtl->current_track_gc )
1791 g_object_unref ( vtl->current_track_gc );
1792 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1793 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1795 // 'newpoint' gc is exactly the same as the current track gc
1796 if ( vtl->current_track_newpoint_gc )
1797 g_object_unref ( vtl->current_track_newpoint_gc );
1798 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1799 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1801 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1803 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1804 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1806 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1807 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1808 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1810 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1813 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1815 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1816 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1818 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1819 /* early exit, as the rest is GUI related */
1823 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1824 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1826 trw_layer_new_track_gcs ( rv, vp );
1828 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1829 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1830 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1831 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1833 rv->has_verified_thumbnails = FALSE;
1834 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1836 rv->wp_draw_symbols = TRUE;
1837 rv->wp_font_size = FS_MEDIUM;
1839 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1841 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1846 #define SMALL_ICON_SIZE 18
1848 * Can accept a null symbol, and may return null value
1850 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1852 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1853 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1854 // So needing a small icon for the treeview may need some resizing:
1855 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1856 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1860 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1862 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1864 GdkPixbuf *pixbuf = NULL;
1866 if ( track->has_color ) {
1867 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1868 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1869 // Here is some magic found to do the conversion
1870 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1871 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1872 ((track->color.green & 0xff00) << 8) |
1873 (track->color.blue & 0xff00);
1875 gdk_pixbuf_fill ( pixbuf, pixel );
1878 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1879 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
1881 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
1885 g_object_unref (pixbuf);
1887 *new_iter = *((GtkTreeIter *) pass_along[1]);
1888 if ( track->is_route )
1889 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1891 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1893 if ( ! track->visible )
1894 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1897 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1899 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1901 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1902 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
1904 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
1907 *new_iter = *((GtkTreeIter *) pass_along[1]);
1908 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1910 if ( ! wp->visible )
1911 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1914 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1916 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1917 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1919 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1923 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1925 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1926 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1928 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1932 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1934 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1935 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1937 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1941 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1944 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1946 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1947 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1948 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1950 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1953 if ( g_hash_table_size (vtl->routes) > 0 ) {
1955 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
1957 pass_along[0] = &(vtl->routes_iter);
1958 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
1960 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
1962 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
1965 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
1966 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
1968 pass_along[0] = &(vtl->waypoints_iter);
1969 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1971 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1973 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1978 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1982 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1983 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1984 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
1985 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1987 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1989 return (t->visible ^= 1);
1993 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1995 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1997 return (t->visible ^= 1);
2001 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2003 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2005 return (t->visible ^= 1);
2014 * Return a property about tracks for this layer
2016 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2018 return vtl->line_thickness;
2021 // Structure to hold multiple track information for a layer
2030 * Build up layer multiple track information via updating the tooltip_tracks structure
2032 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2034 tt->length = tt->length + vik_track_get_length (tr);
2036 // Ensure times are available
2037 if ( tr->trackpoints &&
2038 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2039 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2042 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2043 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2045 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2046 // Hence initialize to the first 'proper' value
2047 if ( tt->start_time == 0 )
2048 tt->start_time = t1;
2049 if ( tt->end_time == 0 )
2052 // Update find the earliest / last times
2053 if ( t1 < tt->start_time )
2054 tt->start_time = t1;
2055 if ( t2 > tt->end_time )
2058 // Keep track of total time
2059 // there maybe gaps within a track (eg segments)
2060 // but this should be generally good enough for a simple indicator
2061 tt->duration = tt->duration + (int)(t2-t1);
2066 * Generate tooltip text for the layer.
2067 * This is relatively complicated as it considers information for
2068 * no tracks, a single track or multiple tracks
2069 * (which may or may not have timing information)
2071 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2082 static gchar tmp_buf[128];
2085 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2087 // Safety check - I think these should always be valid
2088 if ( vtl->tracks && vtl->waypoints ) {
2089 tooltip_tracks tt = { 0.0, 0, 0 };
2090 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2092 GDate* gdate_start = g_date_new ();
2093 g_date_set_time_t (gdate_start, tt.start_time);
2095 GDate* gdate_end = g_date_new ();
2096 g_date_set_time_t (gdate_end, tt.end_time);
2098 if ( g_date_compare (gdate_start, gdate_end) ) {
2099 // Dates differ so print range on separate line
2100 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2101 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2102 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2105 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2106 if ( tt.start_time != 0 )
2107 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2111 if ( tt.length > 0.0 ) {
2112 gdouble len_in_units;
2114 // Setup info dependent on distance units
2115 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2116 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2117 len_in_units = VIK_METERS_TO_MILES(tt.length);
2120 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2121 len_in_units = tt.length/1000.0;
2124 // Timing information if available
2126 if ( tt.duration > 0 ) {
2127 g_snprintf (tbuf1, sizeof(tbuf1),
2128 _(" in %d:%02d hrs:mins"),
2129 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2131 g_snprintf (tbuf2, sizeof(tbuf2),
2132 _("\n%sTotal Length %.1f %s%s"),
2133 tbuf3, len_in_units, tbuf4, tbuf1);
2136 // Put together all the elements to form compact tooltip text
2137 g_snprintf (tmp_buf, sizeof(tmp_buf),
2138 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2139 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2141 g_date_free (gdate_start);
2142 g_date_free (gdate_end);
2149 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2153 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2155 // Very simple tooltip - may expand detail in the future...
2156 static gchar tmp_buf[32];
2157 g_snprintf (tmp_buf, sizeof(tmp_buf),
2159 g_hash_table_size (l->tracks));
2163 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2165 // Very simple tooltip - may expand detail in the future...
2166 static gchar tmp_buf[32];
2167 g_snprintf (tmp_buf, sizeof(tmp_buf),
2169 g_hash_table_size (l->routes));
2174 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2175 // Same tooltip for a route
2176 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2179 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2180 tr = g_hash_table_lookup ( l->tracks, sublayer );
2182 tr = g_hash_table_lookup ( l->routes, sublayer );
2185 // Could be a better way of handling strings - but this works...
2186 gchar time_buf1[20];
2187 gchar time_buf2[20];
2188 time_buf1[0] = '\0';
2189 time_buf2[0] = '\0';
2190 static gchar tmp_buf[100];
2191 // Compact info: Short date eg (11/20/99), duration and length
2192 // Hopefully these are the things that are most useful and so promoted into the tooltip
2193 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2194 // %x The preferred date representation for the current locale without the time.
2195 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2196 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2197 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2199 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2202 // Get length and consider the appropriate distance units
2203 gdouble tr_len = vik_track_get_length(tr);
2204 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2205 switch (dist_units) {
2206 case VIK_UNITS_DISTANCE_KILOMETRES:
2207 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2209 case VIK_UNITS_DISTANCE_MILES:
2210 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2219 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2221 // Very simple tooltip - may expand detail in the future...
2222 static gchar tmp_buf[32];
2223 g_snprintf (tmp_buf, sizeof(tmp_buf),
2225 g_hash_table_size (l->waypoints));
2229 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2231 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2232 // NB It's OK to return NULL
2243 * Function to show basic track point information on the statusbar
2245 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2248 switch (a_vik_get_units_height ()) {
2249 case VIK_UNITS_HEIGHT_FEET:
2250 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2253 //VIK_UNITS_HEIGHT_METRES:
2254 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2259 if ( trkpt->has_timestamp ) {
2260 // Compact date time format
2261 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2265 // Position is put later on, as this bit may not be seen if the display is not big enough,
2266 // one can easily use the current pointer position to see this if needed
2267 gchar *lat = NULL, *lon = NULL;
2268 static struct LatLon ll;
2269 vik_coord_to_latlon (&(trkpt->coord), &ll);
2270 a_coords_latlon_to_string ( &ll, &lat, &lon );
2273 // Again is put later on, as this bit may not be seen if the display is not big enough
2274 // trackname can be seen from the treeview (when enabled)
2275 // Also name could be very long to not leave room for anything else
2278 if ( vtl->current_tp_track ) {
2279 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2282 // Combine parts to make overall message
2283 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2284 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2291 * Function to show basic waypoint information on the statusbar
2293 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2296 switch (a_vik_get_units_height ()) {
2297 case VIK_UNITS_HEIGHT_FEET:
2298 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2301 //VIK_UNITS_HEIGHT_METRES:
2302 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2306 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2307 // one can easily use the current pointer position to see this if needed
2308 gchar *lat = NULL, *lon = NULL;
2309 static struct LatLon ll;
2310 vik_coord_to_latlon (&(wpt->coord), &ll);
2311 a_coords_latlon_to_string ( &ll, &lat, &lon );
2313 // Combine parts to make overall message
2316 // Add comment if available
2317 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2319 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2320 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2327 * General layer selection function, find out which bit is selected and take appropriate action
2329 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2332 l->current_wp = NULL;
2333 l->current_wp_id = NULL;
2334 trw_layer_cancel_current_tp ( l, FALSE );
2337 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2341 case VIK_TREEVIEW_TYPE_LAYER:
2343 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2344 /* Mark for redraw */
2349 case VIK_TREEVIEW_TYPE_SUBLAYER:
2353 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2355 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2356 /* Mark for redraw */
2360 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2362 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2363 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2364 /* Mark for redraw */
2368 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2370 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2371 /* Mark for redraw */
2375 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2377 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2378 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2379 /* Mark for redraw */
2383 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2385 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2386 /* Mark for redraw */
2390 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2392 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2394 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2395 // Show some waypoint info
2396 set_statusbar_msg_info_wpt ( l, wpt );
2397 /* Mark for redraw */
2404 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2413 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2418 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2423 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2428 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2430 return l->waypoints;
2434 * ATM use a case sensitive find
2435 * Finds the first one
2437 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2439 if ( wp && wp->name )
2440 if ( ! strcmp ( wp->name, name ) )
2446 * Get waypoint by name - not guaranteed to be unique
2447 * Finds the first one
2449 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2451 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2455 * ATM use a case sensitive find
2456 * Finds the first one
2458 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2460 if ( trk && trk->name )
2461 if ( ! strcmp ( trk->name, name ) )
2467 * Get track by name - not guaranteed to be unique
2468 * Finds the first one
2470 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2472 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2476 * Get route by name - not guaranteed to be unique
2477 * Finds the first one
2479 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2481 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2484 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] )
2486 static VikCoord fixme;
2487 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2488 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2489 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2490 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2491 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2492 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2493 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2494 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2495 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2498 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2500 GList *tr = trk->trackpoints;
2501 static VikCoord fixme;
2505 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2506 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2507 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2508 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2509 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2510 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2511 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2512 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2513 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2518 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2520 // Continually reuse maxmin to find the latest maximum and minimum values
2521 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2522 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2523 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2526 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2528 /* 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... */
2529 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2530 trw_layer_find_maxmin (vtl, maxmin);
2531 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2535 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2536 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2541 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2544 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2545 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2547 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2550 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2552 /* First set the center [in case previously viewing from elsewhere] */
2553 /* Then loop through zoom levels until provided positions are in view */
2554 /* This method is not particularly fast - but should work well enough */
2555 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2557 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2558 vik_viewport_set_center_coord ( vvp, &coord );
2560 /* Convert into definite 'smallest' and 'largest' positions */
2561 struct LatLon minmin;
2562 if ( maxmin[0].lat < maxmin[1].lat )
2563 minmin.lat = maxmin[0].lat;
2565 minmin.lat = maxmin[1].lat;
2567 struct LatLon maxmax;
2568 if ( maxmin[0].lon > maxmin[1].lon )
2569 maxmax.lon = maxmin[0].lon;
2571 maxmax.lon = maxmin[1].lon;
2573 /* Never zoom in too far - generally not that useful, as too close ! */
2574 /* Always recalculate the 'best' zoom level */
2576 vik_viewport_set_zoom ( vvp, zoom );
2578 gdouble min_lat, max_lat, min_lon, max_lon;
2579 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2580 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2581 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2582 /* NB I think the logic used in this test to determine if the bounds is within view
2583 fails if track goes across 180 degrees longitude.
2584 Hopefully that situation is not too common...
2585 Mind you viking doesn't really do edge locations to well anyway */
2586 if ( min_lat < minmin.lat &&
2587 max_lat > minmin.lat &&
2588 min_lon < maxmax.lon &&
2589 max_lon > maxmax.lon )
2590 /* Found within zoom level */
2595 vik_viewport_set_zoom ( vvp, zoom );
2599 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2601 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2602 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2603 trw_layer_find_maxmin (vtl, maxmin);
2604 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2607 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2612 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2614 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])) ) ) {
2615 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2618 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2621 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2623 GtkWidget *file_selector;
2625 gboolean failed = FALSE;
2626 file_selector = gtk_file_chooser_dialog_new (title,
2628 GTK_FILE_CHOOSER_ACTION_SAVE,
2629 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2630 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2632 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2634 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2636 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2637 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2639 gtk_widget_hide ( file_selector );
2640 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2645 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2647 gtk_widget_hide ( file_selector );
2648 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2653 gtk_widget_destroy ( file_selector );
2655 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2658 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2660 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2663 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2665 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2668 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2670 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2671 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2672 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2673 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2675 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2677 g_free ( auto_save_name );
2680 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2682 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2683 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2684 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2685 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2687 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2689 g_free ( auto_save_name );
2693 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2696 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2698 gchar *name_used = NULL;
2701 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2702 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2704 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2708 gchar *quoted_file = g_shell_quote ( name_used );
2709 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2710 g_free ( quoted_file );
2711 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2713 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2714 g_error_free ( err );
2718 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2719 //g_remove ( name_used );
2720 // Perhaps should be deleted when the program ends?
2721 // For now leave it to the user to delete it / use system temp cleanup methods.
2722 g_free ( name_used );
2726 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2728 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2731 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2733 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2736 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2738 gpointer layer_and_vlp[2];
2739 layer_and_vlp[0] = pass_along[0];
2740 layer_and_vlp[1] = pass_along[1];
2742 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2744 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2745 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2747 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2749 if ( !trk || !trk->name )
2752 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2753 gchar *auto_save_name = g_strdup ( trk->name );
2754 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2755 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2757 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2759 g_free ( auto_save_name );
2763 VikWaypoint *wp; // input
2764 gpointer uuid; // output
2767 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2769 wpu_udata *user_data = udata;
2770 if ( wp == user_data->wp ) {
2771 user_data->uuid = id;
2777 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2779 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2780 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2781 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2783 GTK_RESPONSE_REJECT,
2785 GTK_RESPONSE_ACCEPT,
2788 GtkWidget *label, *entry;
2789 label = gtk_label_new(_("Waypoint Name:"));
2790 entry = gtk_entry_new();
2792 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2793 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2794 gtk_widget_show_all ( label );
2795 gtk_widget_show_all ( entry );
2797 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2799 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2801 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2802 // Find *first* wp with the given name
2803 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2806 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2809 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2810 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2812 // Find and select on the side panel
2817 // Hmmm, want key of it
2818 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2820 if ( wpf && udata.uuid ) {
2821 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2822 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2831 gtk_widget_destroy ( dia );
2834 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2836 gchar *default_name = highest_wp_number_get(vtl);
2837 VikWaypoint *wp = vik_waypoint_new();
2838 gchar *returned_name;
2840 wp->coord = *def_coord;
2842 // Attempt to auto set height if DEM data is available
2843 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2844 if ( elev != VIK_DEM_INVALID_ELEVATION )
2845 wp->altitude = (gdouble)elev;
2847 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2849 if ( returned_name )
2852 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2853 g_free (default_name);
2854 g_free (returned_name);
2857 g_free (default_name);
2858 vik_waypoint_free(wp);
2862 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2865 struct LatLon one_ll, two_ll;
2866 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2868 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2869 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2870 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2871 VikViewport *vvp = vik_window_viewport(vw);
2872 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2873 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2874 vik_coord_to_latlon(&one, &one_ll);
2875 vik_coord_to_latlon(&two, &two_ll);
2876 if (one_ll.lat > two_ll.lat) {
2877 maxmin[0].lat = one_ll.lat;
2878 maxmin[1].lat = two_ll.lat;
2881 maxmin[0].lat = two_ll.lat;
2882 maxmin[1].lat = one_ll.lat;
2884 if (one_ll.lon > two_ll.lon) {
2885 maxmin[0].lon = one_ll.lon;
2886 maxmin[1].lon = two_ll.lon;
2889 maxmin[0].lon = two_ll.lon;
2890 maxmin[1].lon = one_ll.lon;
2892 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2893 vik_layers_panel_emit_update ( vlp );
2896 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2898 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2899 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2900 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2902 trw_layer_find_maxmin (vtl, maxmin);
2903 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2904 vik_layers_panel_emit_update ( vlp );
2907 #ifdef VIK_CONFIG_GEOTAG
2908 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2910 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2912 // Update directly - not changing the mtime
2913 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2916 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2918 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2921 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2925 * Use code in separate file for this feature as reasonably complex
2927 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2929 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2930 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2931 // Unset so can be reverified later if necessary
2932 vtl->has_verified_thumbnails = FALSE;
2934 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2940 static void trw_layer_geotagging ( gpointer lav[2] )
2942 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2943 // Unset so can be reverified later if necessary
2944 vtl->has_verified_thumbnails = FALSE;
2946 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2953 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2956 * Acquire into this TRW Layer straight from GPS Device
2958 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2960 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2961 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2962 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2963 VikViewport *vvp = vik_window_viewport(vw);
2965 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2966 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2969 #ifdef VIK_CONFIG_GOOGLE
2971 * Acquire into this TRW Layer from Google Directions
2973 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2975 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2976 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2977 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2978 VikViewport *vvp = vik_window_viewport(vw);
2980 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2984 #ifdef VIK_CONFIG_OPENSTREETMAP
2986 * Acquire into this TRW Layer from OSM
2988 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2990 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2991 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2992 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2993 VikViewport *vvp = vik_window_viewport(vw);
2995 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2999 #ifdef VIK_CONFIG_GEOCACHES
3001 * Acquire into this TRW Layer from Geocaching.com
3003 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3005 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3006 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3007 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3008 VikViewport *vvp = vik_window_viewport(vw);
3010 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
3014 #ifdef VIK_CONFIG_GEOTAG
3016 * Acquire into this TRW Layer from images
3018 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3020 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3021 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3022 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3023 VikViewport *vvp = vik_window_viewport(vw);
3025 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3026 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
3028 // Reverify thumbnails as they may have changed
3029 vtl->has_verified_thumbnails = FALSE;
3030 trw_layer_verify_thumbnails ( vtl, NULL );
3034 static void trw_layer_gps_upload ( gpointer lav[2] )
3036 gpointer pass_along[6];
3037 pass_along[0] = lav[0];
3038 pass_along[1] = lav[1];
3039 pass_along[2] = NULL; // No track - operate on the layer
3040 pass_along[3] = NULL;
3041 pass_along[4] = NULL;
3042 pass_along[5] = NULL;
3044 trw_layer_gps_upload_any ( pass_along );
3048 * If pass_along[3] is defined that this will upload just that track
3050 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3052 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3053 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3055 // May not actually get a track here as pass_along[2&3] can be null
3056 VikTrack *track = NULL;
3057 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3058 gboolean xfer_all = FALSE;
3060 if ( pass_along[2] ) {
3062 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3063 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3066 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3067 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3070 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3073 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3077 else if ( !pass_along[4] )
3078 xfer_all = TRUE; // i.e. whole layer
3080 if (track && !track->visible) {
3081 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3085 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3086 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3087 GTK_DIALOG_DESTROY_WITH_PARENT,
3089 GTK_RESPONSE_ACCEPT,
3091 GTK_RESPONSE_REJECT,
3094 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3095 GtkWidget *response_w = NULL;
3096 #if GTK_CHECK_VERSION (2, 20, 0)
3097 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3101 gtk_widget_grab_focus ( response_w );
3103 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3105 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3106 datasource_gps_clean_up ( dgs );
3107 gtk_widget_destroy ( dialog );
3111 // Get info from reused datasource dialog widgets
3112 gchar* protocol = datasource_gps_get_protocol ( dgs );
3113 gchar* port = datasource_gps_get_descriptor ( dgs );
3114 // NB don't free the above strings as they're references to values held elsewhere
3115 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3116 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3117 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3118 gboolean turn_off = datasource_gps_get_off ( dgs );
3120 gtk_widget_destroy ( dialog );
3122 // When called from the viewport - work the corresponding layerspanel:
3124 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3127 // Apply settings to transfer to the GPS device
3134 vik_layers_panel_get_viewport (vlp),
3143 * Acquire into this TRW Layer from any GPS Babel supported file
3145 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3147 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3148 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3149 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3150 VikViewport *vvp = vik_window_viewport(vw);
3152 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3155 static void trw_layer_new_wp ( gpointer lav[2] )
3157 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3158 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3159 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3160 instead return true if you want to update. */
3161 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 )
3162 vik_layers_panel_emit_update ( vlp );
3165 static void trw_layer_new_track ( gpointer lav[2] )
3167 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3169 if ( ! vtl->current_track ) {
3170 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3171 vtl->current_track = vik_track_new();
3172 vtl->current_track->visible = TRUE;
3173 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3175 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3179 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3181 vtl->current_track = vik_track_new();
3182 vtl->current_track->visible = TRUE;
3183 vtl->current_track->is_route = TRUE;
3184 // By default make all routes red
3185 vtl->current_track->has_color = TRUE;
3186 gdk_color_parse ( "red", &vtl->current_track->color );
3187 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3190 static void trw_layer_new_route ( gpointer lav[2] )
3192 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3194 if ( ! vtl->current_track ) {
3195 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3196 new_route_create_common ( vtl, name );
3197 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3201 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3203 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3204 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3206 if ( g_hash_table_size (vtl->routes) > 0 ) {
3207 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3208 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3209 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3210 vik_layers_panel_emit_update ( vlp );
3215 static void trw_layer_finish_track ( gpointer lav[2] )
3217 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3218 vtl->current_track = NULL;
3219 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3222 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3224 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3225 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3227 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3228 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3229 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3230 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3231 vik_layers_panel_emit_update ( vlp );
3235 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3237 /* NB do not care if wp is visible or not */
3238 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3241 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3243 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3244 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3246 /* Only 1 waypoint - jump straight to it */
3247 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3248 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3249 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3251 /* If at least 2 waypoints - find center and then zoom to fit */
3252 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3254 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3255 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3256 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3259 vik_layers_panel_emit_update ( vlp );
3262 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3264 static gpointer pass_along[2];
3266 GtkWidget *export_submenu;
3267 pass_along[0] = vtl;
3268 pass_along[1] = vlp;
3270 item = gtk_menu_item_new();
3271 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3272 gtk_widget_show ( item );
3274 if ( vtl->current_track ) {
3275 if ( vtl->current_track->is_route )
3276 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3278 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3280 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3281 gtk_widget_show ( item );
3284 item = gtk_menu_item_new ();
3285 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3286 gtk_widget_show ( item );
3289 /* Now with icons */
3290 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3291 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3292 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3293 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3294 gtk_widget_show ( item );
3296 GtkWidget *view_submenu = gtk_menu_new();
3297 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3298 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3299 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3300 gtk_widget_show ( item );
3301 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3303 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3305 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3306 gtk_widget_show ( item );
3308 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3309 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3310 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3311 gtk_widget_show ( item );
3313 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3314 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3315 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3316 gtk_widget_show ( item );
3318 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3319 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3320 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3321 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3322 gtk_widget_show ( item );
3324 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3325 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3326 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3327 gtk_widget_show ( item );
3329 export_submenu = gtk_menu_new ();
3330 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3331 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3332 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3333 gtk_widget_show ( item );
3334 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3336 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3337 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3338 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3339 gtk_widget_show ( item );
3341 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3343 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3344 gtk_widget_show ( item );
3346 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3347 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3348 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3349 gtk_widget_show ( item );
3351 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3353 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3354 gtk_widget_show ( item );
3356 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3357 item = gtk_menu_item_new_with_mnemonic ( external1 );
3358 g_free ( external1 );
3359 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3360 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3361 gtk_widget_show ( item );
3363 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3364 item = gtk_menu_item_new_with_mnemonic ( external2 );
3365 g_free ( external2 );
3366 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3367 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3368 gtk_widget_show ( item );
3370 GtkWidget *new_submenu = gtk_menu_new();
3371 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3372 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3373 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3374 gtk_widget_show(item);
3375 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3377 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3378 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3380 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3381 gtk_widget_show ( item );
3383 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3384 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3386 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3387 gtk_widget_show ( item );
3388 // Make it available only when a new track *not* already in progress
3389 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3391 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3392 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3394 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3395 gtk_widget_show ( item );
3396 // Make it available only when a new track *not* already in progress
3397 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3399 #ifdef VIK_CONFIG_GEONAMES
3400 GtkWidget *wikipedia_submenu = gtk_menu_new();
3401 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3402 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3403 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3404 gtk_widget_show(item);
3405 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3407 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3408 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3409 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3410 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3411 gtk_widget_show ( item );
3413 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3414 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3415 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3416 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3417 gtk_widget_show ( item );
3420 #ifdef VIK_CONFIG_GEOTAG
3421 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3423 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3424 gtk_widget_show ( item );
3427 GtkWidget *acquire_submenu = gtk_menu_new ();
3428 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3429 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3430 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3431 gtk_widget_show ( item );
3432 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3434 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3435 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3436 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3437 gtk_widget_show ( item );
3439 #ifdef VIK_CONFIG_GOOGLE
3440 item = gtk_menu_item_new_with_mnemonic ( _("From Google _Directions...") );
3441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3442 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3443 gtk_widget_show ( item );
3446 #ifdef VIK_CONFIG_OPENSTREETMAP
3447 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3449 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3450 gtk_widget_show ( item );
3453 #ifdef VIK_CONFIG_GEOCACHES
3454 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3455 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3456 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3457 gtk_widget_show ( item );
3460 #ifdef VIK_CONFIG_GEOTAG
3461 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3462 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3463 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3464 gtk_widget_show ( item );
3467 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3468 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3469 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3470 gtk_widget_show ( item );
3472 GtkWidget *upload_submenu = gtk_menu_new ();
3473 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3474 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3475 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3476 gtk_widget_show ( item );
3477 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3479 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3480 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3481 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3482 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3483 gtk_widget_show ( item );
3485 #ifdef VIK_CONFIG_OPENSTREETMAP
3486 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3489 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3490 gtk_widget_show ( item );
3493 GtkWidget *delete_submenu = gtk_menu_new ();
3494 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3495 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3496 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3497 gtk_widget_show ( item );
3498 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3500 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3501 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3502 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3503 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3504 gtk_widget_show ( item );
3506 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3507 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3508 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3509 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3510 gtk_widget_show ( item );
3512 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3513 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3514 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3515 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3516 gtk_widget_show ( item );
3518 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3519 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3520 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3521 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3522 gtk_widget_show ( item );
3524 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3525 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3527 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3528 gtk_widget_show ( item );
3531 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3532 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3534 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3535 gtk_widget_show ( item );
3539 // Fake Waypoint UUIDs vi simple increasing integer
3540 static guint wp_uuid = 0;
3542 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3546 vik_waypoint_set_name (wp, name);
3548 if ( VIK_LAYER(vtl)->realized )
3550 // Do we need to create the sublayer:
3551 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3552 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3555 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3557 // Visibility column always needed for waypoints
3558 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3559 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
3561 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
3563 // Actual setting of visibility dependent on the waypoint
3564 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3566 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3569 highest_wp_number_add_wp(vtl, name);
3570 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3574 // Fake Track UUIDs vi simple increasing integer
3575 static guint tr_uuid = 0;
3577 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3581 vik_track_set_name (t, name);
3583 if ( VIK_LAYER(vtl)->realized )
3585 // Do we need to create the sublayer:
3586 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3587 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3590 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3591 // Visibility column always needed for tracks
3592 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3593 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 );
3595 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 );
3597 // Actual setting of visibility dependent on the track
3598 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3600 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3603 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3605 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3608 // Fake Route UUIDs vi simple increasing integer
3609 static guint rt_uuid = 0;
3611 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3615 vik_track_set_name (t, name);
3617 if ( VIK_LAYER(vtl)->realized )
3619 // Do we need to create the sublayer:
3620 if ( g_hash_table_size (vtl->routes) == 0 ) {
3621 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3624 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3625 // Visibility column always needed for tracks
3626 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3627 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3629 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3631 // Actual setting of visibility dependent on the track
3632 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3634 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3637 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3639 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3642 /* to be called whenever a track has been deleted or may have been changed. */
3643 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3645 if (vtl->current_tp_track == trk )
3646 trw_layer_cancel_current_tp ( vtl, FALSE );
3649 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3652 gchar *newname = g_strdup(name);
3657 switch ( sublayer_type ) {
3658 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3659 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3661 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3662 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3665 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3668 // If found a name already in use try adding 1 to it and we try again
3670 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3672 newname = new_newname;
3675 } while ( id != NULL);
3680 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3682 // No more uniqueness of name forced when loading from a file
3683 // This now makes this function a little redunant as we just flow the parameters through
3684 vik_trw_layer_add_waypoint ( vtl, name, wp );
3687 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3689 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3690 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3691 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3692 vik_track_free ( tr );
3693 vtl->route_finder_append = FALSE; /* this means we have added it */
3696 // No more uniqueness of name forced when loading from a file
3698 vik_trw_layer_add_route ( vtl, name, tr );
3700 vik_trw_layer_add_track ( vtl, name, tr );
3702 if ( vtl->route_finder_check_added_track ) {
3703 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3704 vtl->route_finder_added_track = tr;
3709 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3711 *l = g_list_append(*l, id);
3715 * Move an item from one TRW layer to another TRW layer
3717 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3719 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3720 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3722 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3724 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3725 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3726 vik_trw_layer_delete_track ( vtl_src, trk );
3729 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3730 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3732 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3734 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3735 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3736 vik_trw_layer_delete_route ( vtl_src, trk );
3739 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3740 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3742 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3744 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3745 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3746 trw_layer_delete_waypoint ( vtl_src, wp );
3750 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3752 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3753 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3755 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3756 GList *items = NULL;
3759 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3760 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3762 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3763 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3765 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3766 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3771 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3772 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3774 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3775 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3777 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3784 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3785 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3790 VikTrack *trk; // input
3791 gpointer uuid; // output
3794 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3796 trku_udata *user_data = udata;
3797 if ( trk == user_data->trk ) {
3798 user_data->uuid = id;
3804 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3806 gboolean was_visible = FALSE;
3808 if ( trk && trk->name ) {
3810 if ( trk == vtl->current_track ) {
3811 vtl->current_track = NULL;
3812 vtl->current_tp_track = NULL;
3813 vtl->current_tp_id = NULL;
3814 vtl->moving_tp = FALSE;
3817 was_visible = trk->visible;
3819 if ( trk == vtl->route_finder_current_track )
3820 vtl->route_finder_current_track = NULL;
3822 if ( trk == vtl->route_finder_added_track )
3823 vtl->route_finder_added_track = NULL;
3829 // Hmmm, want key of it
3830 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3832 if ( trkf && udata.uuid ) {
3833 /* could be current_tp, so we have to check */
3834 trw_layer_cancel_tps_of_track ( vtl, trk );
3836 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3839 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3840 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3841 g_hash_table_remove ( vtl->tracks, udata.uuid );
3843 // If last sublayer, then remove sublayer container
3844 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3845 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3853 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
3855 gboolean was_visible = FALSE;
3857 if ( trk && trk->name ) {
3859 if ( trk == vtl->current_track ) {
3860 vtl->current_track = NULL;
3861 vtl->current_tp_track = NULL;
3862 vtl->current_tp_id = NULL;
3863 vtl->moving_tp = FALSE;
3866 was_visible = trk->visible;
3868 if ( trk == vtl->route_finder_current_track )
3869 vtl->route_finder_current_track = NULL;
3871 if ( trk == vtl->route_finder_added_track )
3872 vtl->route_finder_added_track = NULL;
3878 // Hmmm, want key of it
3879 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
3881 if ( trkf && udata.uuid ) {
3882 /* could be current_tp, so we have to check */
3883 trw_layer_cancel_tps_of_track ( vtl, trk );
3885 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
3888 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3889 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
3890 g_hash_table_remove ( vtl->routes, udata.uuid );
3892 // If last sublayer, then remove sublayer container
3893 if ( g_hash_table_size (vtl->routes) == 0 ) {
3894 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
3902 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3904 gboolean was_visible = FALSE;
3906 if ( wp && wp->name ) {
3908 if ( wp == vtl->current_wp ) {
3909 vtl->current_wp = NULL;
3910 vtl->current_wp_id = NULL;
3911 vtl->moving_wp = FALSE;
3914 was_visible = wp->visible;
3920 // Hmmm, want key of it
3921 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3923 if ( wpf && udata.uuid ) {
3924 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3927 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3928 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3930 highest_wp_number_remove_wp(vtl, wp->name);
3931 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3933 // If last sublayer, then remove sublayer container
3934 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3935 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3945 // Only for temporary use by trw_layer_delete_waypoint_by_name
3946 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3948 wpu_udata *user_data = udata;
3949 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3950 user_data->uuid = id;
3957 * Delete a waypoint by the given name
3958 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3959 * as there be multiple waypoints with the same name
3961 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3964 // Fake a waypoint with the given name
3965 udata.wp = vik_waypoint_new ();
3966 vik_waypoint_set_name (udata.wp, name);
3967 // Currently only the name is used in this waypoint find function
3970 // Hmmm, want key of it
3971 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3973 vik_waypoint_free (udata.wp);
3975 if ( wpf && udata.uuid )
3976 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3982 VikTrack *trk; // input
3983 gpointer uuid; // output
3986 // Only for temporary use by trw_layer_delete_track_by_name
3987 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3989 tpu_udata *user_data = udata;
3990 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3991 user_data->uuid = id;
3998 * Delete a track by the given name
3999 * NOTE: ATM this will delete the first encountered Track with the specified name
4000 * as there may be multiple tracks with the same name within the specified hash table
4002 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4005 // Fake a track with the given name
4006 udata.trk = vik_track_new ();
4007 vik_track_set_name (udata.trk, name);
4008 // Currently only the name is used in this waypoint find function
4011 // Hmmm, want key of it
4012 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4014 vik_track_free (udata.trk);
4016 if ( trkf && udata.uuid ) {
4017 // This could be a little better written...
4018 if ( vtl->tracks == ht_tracks )
4019 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4020 if ( vtl->routes == ht_tracks )
4021 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4028 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4030 vik_treeview_item_delete (vt, it );
4033 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4036 vtl->current_track = NULL;
4037 vtl->route_finder_current_track = NULL;
4038 vtl->route_finder_added_track = NULL;
4039 if (vtl->current_tp_track)
4040 trw_layer_cancel_current_tp(vtl, FALSE);
4042 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4043 g_hash_table_remove_all(vtl->routes_iters);
4044 g_hash_table_remove_all(vtl->routes);
4046 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4048 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4051 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4054 vtl->current_track = NULL;
4055 vtl->route_finder_current_track = NULL;
4056 vtl->route_finder_added_track = NULL;
4057 if (vtl->current_tp_track)
4058 trw_layer_cancel_current_tp(vtl, FALSE);
4060 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4061 g_hash_table_remove_all(vtl->tracks_iters);
4062 g_hash_table_remove_all(vtl->tracks);
4064 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4066 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4069 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4071 vtl->current_wp = NULL;
4072 vtl->current_wp_id = NULL;
4073 vtl->moving_wp = FALSE;
4075 highest_wp_number_reset(vtl);
4077 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4078 g_hash_table_remove_all(vtl->waypoints_iters);
4079 g_hash_table_remove_all(vtl->waypoints);
4081 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4083 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4086 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4088 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4089 // Get confirmation from the user
4090 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4091 _("Are you sure you want to delete all tracks in %s?"),
4092 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4093 vik_trw_layer_delete_all_tracks (vtl);
4096 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4098 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4099 // Get confirmation from the user
4100 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4101 _("Are you sure you want to delete all routes in %s?"),
4102 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4103 vik_trw_layer_delete_all_routes (vtl);
4106 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4108 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4109 // Get confirmation from the user
4110 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4111 _("Are you sure you want to delete all waypoints in %s?"),
4112 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4113 vik_trw_layer_delete_all_waypoints (vtl);
4116 static void trw_layer_delete_item ( gpointer pass_along[6] )
4118 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4119 gboolean was_visible = FALSE;
4120 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4122 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4123 if ( wp && wp->name ) {
4124 if ( GPOINTER_TO_INT ( pass_along[4]) )
4125 // Get confirmation from the user
4126 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4127 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4128 _("Are you sure you want to delete the waypoint \"%s\""),
4131 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4134 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4136 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4137 if ( trk && trk->name ) {
4138 if ( GPOINTER_TO_INT ( pass_along[4]) )
4139 // Get confirmation from the user
4140 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4141 _("Are you sure you want to delete the track \"%s\""),
4144 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4149 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4150 if ( trk && trk->name ) {
4151 if ( GPOINTER_TO_INT ( pass_along[4]) )
4152 // Get confirmation from the user
4153 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4154 _("Are you sure you want to delete the route \"%s\""),
4157 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4161 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4165 static void trw_layer_properties_item ( gpointer pass_along[7] )
4167 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4168 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4170 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4172 if ( wp && wp->name )
4174 gboolean updated = FALSE;
4175 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
4177 if ( updated && wp->symbol && pass_along[6] )
4178 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4180 if ( updated && VIK_LAYER(vtl)->visible )
4181 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4187 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4188 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4190 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4192 if ( tr && tr->name )
4194 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4197 pass_along[1], /* vlp */
4198 pass_along[5], /* vvp */
4199 pass_along[6]); /* iter */
4205 * Update the treeview of the track id - primarily to update the icon
4207 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4213 gpointer *trkf = NULL;
4214 if ( trk->is_route )
4215 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4217 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4219 if ( trkf && udata.uuid ) {
4221 GtkTreeIter *iter = NULL;
4222 if ( trk->is_route )
4223 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4225 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4228 // TODO: Make this a function
4229 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4230 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4231 ((trk->color.green & 0xff00) << 8) |
4232 (trk->color.blue & 0xff00);
4233 gdk_pixbuf_fill ( pixbuf, pixel );
4234 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4235 g_object_unref (pixbuf);
4242 Parameter 1 -> VikLayersPanel
4243 Parameter 2 -> VikLayer
4244 Parameter 3 -> VikViewport
4246 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4249 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4250 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4253 /* since vlp not set, vl & vvp should be valid instead! */
4255 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4256 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
4261 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4263 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4265 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4266 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4268 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4270 if ( track && track->trackpoints )
4271 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4274 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4276 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4278 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4279 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4281 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4283 if ( track && track->trackpoints )
4285 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4287 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4288 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4289 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4290 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4291 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4295 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4297 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4299 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4300 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4302 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4307 // Converting a track to a route can be a bit more complicated,
4308 // so give a chance to change our minds:
4309 if ( !trk->is_route &&
4310 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4311 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4313 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4314 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4319 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4322 trk_copy->is_route = !trk_copy->is_route;
4324 // ATM can't set name to self - so must create temporary copy
4325 gchar *name = g_strdup ( trk_copy->name );
4327 // Delete old one and then add new one
4328 if ( trk->is_route ) {
4329 vik_trw_layer_delete_route ( vtl, trk );
4330 vik_trw_layer_add_track ( vtl, name, trk_copy );
4333 // Extra route conversion bits...
4334 vik_track_merge_segments ( trk_copy );
4335 vik_track_to_routepoints ( trk_copy );
4337 vik_trw_layer_delete_track ( vtl, trk );
4338 vik_trw_layer_add_route ( vtl, name, trk_copy );
4342 // Update in case color of track / route changes when moving between sublayers
4343 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4347 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4349 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4351 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4352 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4354 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4359 vtl->current_track = track;
4360 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, track->is_route ? TOOL_CREATE_ROUTE : TOOL_CREATE_TRACK);
4362 if ( track->trackpoints )
4363 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4366 #ifdef VIK_CONFIG_GOOGLE
4368 * extend a track using route finder
4370 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4372 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4373 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4376 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4378 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4379 vtl->route_finder_coord = last_coord;
4380 vtl->route_finder_current_track = track;
4381 vtl->route_finder_started = TRUE;
4383 if ( track->trackpoints )
4384 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4389 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4391 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4392 /* Also warn if overwrite old elevation data */
4393 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4395 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4396 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4398 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4401 vik_track_apply_dem_data ( track );
4404 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4406 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4408 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4409 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4411 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4416 GList *trps = track->trackpoints;
4419 trps = g_list_last(trps);
4420 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4423 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4425 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4427 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4428 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4430 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4435 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4438 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4441 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4443 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4445 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4446 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4448 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4453 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4456 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4459 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4461 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4463 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4464 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4466 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4471 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4474 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4478 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4480 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4482 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4484 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4485 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4487 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4489 if ( trk && trk->trackpoints )
4491 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4492 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4493 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4494 if ( pass_along[1] )
4495 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4497 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4501 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4503 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4504 trw_layer_tpwin_init ( vtl );
4507 /*************************************
4508 * merge/split by time routines
4509 *************************************/
4511 /* called for each key in track hash table.
4512 * If the current track has the same time stamp type, add it to the result,
4513 * except the one pointed by "exclude".
4514 * set exclude to NULL if there is no exclude to check.
4515 * Note that the result is in reverse (for performance reasons).
4520 gboolean with_timestamps;
4522 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4524 twt_udata *user_data = udata;
4525 VikTrackpoint *p1, *p2;
4527 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4531 if (VIK_TRACK(value)->trackpoints) {
4532 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4533 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4535 if ( user_data->with_timestamps ) {
4536 if (!p1->has_timestamp || !p2->has_timestamp) {
4541 // Don't add tracks with timestamps when getting non timestamp tracks
4542 if (p1->has_timestamp || p2->has_timestamp) {
4548 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4551 /* called for each key in track hash table. if original track user_data[1] is close enough
4552 * to the passed one, add it to list in user_data[0]
4554 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4557 VikTrackpoint *p1, *p2;
4558 VikTrack *trk = VIK_TRACK(value);
4560 GList **nearby_tracks = ((gpointer *)user_data)[0];
4561 GList *tpoints = ((gpointer *)user_data)[1];
4564 * detect reasons for not merging, and return
4565 * if no reason is found not to merge, then do it.
4568 // Exclude the original track from the compiled list
4569 if (trk->trackpoints == tpoints) {
4573 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4574 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4576 if (trk->trackpoints) {
4577 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4578 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4580 if (!p1->has_timestamp || !p2->has_timestamp) {
4581 //g_print("no timestamp\n");
4585 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4586 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4587 if (! (abs(t1 - p2->timestamp) < threshold ||
4589 abs(p1->timestamp - t2) < threshold)
4596 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4599 /* comparison function used to sort tracks; a and b are hash table keys */
4600 /* Not actively used - can be restored if needed
4601 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4603 GHashTable *tracks = user_data;
4606 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4607 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4609 if (t1 < t2) return -1;
4610 if (t1 > t2) return 1;
4615 /* comparison function used to sort trackpoints */
4616 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4618 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4620 if (t1 < t2) return -1;
4621 if (t1 > t2) return 1;
4626 * comparison function which can be used to sort tracks or waypoints by name
4628 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4630 const gchar* namea = (const gchar*) a;
4631 const gchar* nameb = (const gchar*) b;
4632 if ( namea == NULL || nameb == NULL)
4635 // Same sort method as used in the vik_treeview_*_alphabetize functions
4636 return strcmp ( namea, nameb );
4640 * Attempt to merge selected track with other tracks specified by the user
4641 * Tracks to merge with must be of the same 'type' as the selected track -
4642 * either all with timestamps, or all without timestamps
4644 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4646 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4647 GList *other_tracks = NULL;
4648 GHashTable *ght_tracks;
4649 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4650 ght_tracks = vtl->routes;
4652 ght_tracks = vtl->tracks;
4654 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4659 if ( !track->trackpoints )
4663 udata.result = &other_tracks;
4664 udata.exclude = track->trackpoints;
4665 // Allow merging with 'similar' time type time tracks
4666 // i.e. either those times, or those without
4667 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4669 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4670 other_tracks = g_list_reverse(other_tracks);
4672 if ( !other_tracks ) {
4673 if ( udata.with_timestamps )
4674 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4676 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4680 // Sort alphabetically for user presentation
4681 // Convert into list of names for usage with dialog function
4682 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4683 GList *other_tracks_names = NULL;
4684 GList *iter = g_list_first ( other_tracks );
4686 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4687 iter = g_list_next ( iter );
4690 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4692 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4696 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4697 g_list_free(other_tracks);
4698 g_list_free(other_tracks_names);
4703 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4704 VikTrack *merge_track;
4705 if ( track->is_route )
4706 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4708 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4711 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4712 merge_track->trackpoints = NULL;
4713 if ( track->is_route )
4714 vik_trw_layer_delete_route (vtl, merge_track);
4716 vik_trw_layer_delete_track (vtl, merge_track);
4717 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4720 /* TODO: free data before free merge_list */
4721 for (l = merge_list; l != NULL; l = g_list_next(l))
4723 g_list_free(merge_list);
4724 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4728 // c.f. trw_layer_sorted_track_id_by_name_list
4729 // but don't add the specified track to the list (normally current track)
4730 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4732 twt_udata *user_data = udata;
4735 if (trk->trackpoints == user_data->exclude) {
4739 // Sort named list alphabetically
4740 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4744 * Join - this allows combining 'tracks' and 'track routes'
4745 * i.e. doesn't care about whether tracks have consistent timestamps
4746 * ATM can only append one track at a time to the currently selected track
4748 static void trw_layer_append_track ( gpointer pass_along[6] )
4751 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4753 GHashTable *ght_tracks;
4754 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4755 ght_tracks = vtl->routes;
4757 ght_tracks = vtl->tracks;
4759 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4764 GList *other_tracks_names = NULL;
4766 // Sort alphabetically for user presentation
4767 // Convert into list of names for usage with dialog function
4768 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4770 udata.result = &other_tracks_names;
4771 udata.exclude = trk->trackpoints;
4773 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4775 // Note the limit to selecting one track only
4776 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4777 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4778 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4781 trk->is_route ? _("Append Route"): _("Append Track"),
4782 trk->is_route ? _("Select the route to append after the current route") :
4783 _("Select the track to append after the current track") );
4785 g_list_free(other_tracks_names);
4787 // It's a list, but shouldn't contain more than one other track!
4788 if ( append_list ) {
4790 for (l = append_list; l != NULL; l = g_list_next(l)) {
4791 // TODO: at present this uses the first track found by name,
4792 // which with potential multiple same named tracks may not be the one selected...
4793 VikTrack *append_track;
4794 if ( trk->is_route )
4795 append_track = vik_trw_layer_get_route ( vtl, l->data );
4797 append_track = vik_trw_layer_get_track ( vtl, l->data );
4799 if ( append_track ) {
4800 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4801 append_track->trackpoints = NULL;
4802 if ( trk->is_route )
4803 vik_trw_layer_delete_route (vtl, append_track);
4805 vik_trw_layer_delete_track (vtl, append_track);
4808 for (l = append_list; l != NULL; l = g_list_next(l))
4810 g_list_free(append_list);
4811 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4816 * Very similar to trw_layer_append_track for joining
4817 * but this allows selection from the 'other' list
4818 * If a track is selected, then is shows routes and joins the selected one
4819 * If a route is selected, then is shows tracks and joins the selected one
4821 static void trw_layer_append_other ( gpointer pass_along[6] )
4824 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4826 GHashTable *ght_mykind, *ght_others;
4827 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4828 ght_mykind = vtl->routes;
4829 ght_others = vtl->tracks;
4832 ght_mykind = vtl->tracks;
4833 ght_others = vtl->routes;
4836 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
4841 GList *other_tracks_names = NULL;
4843 // Sort alphabetically for user presentation
4844 // Convert into list of names for usage with dialog function
4845 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4847 udata.result = &other_tracks_names;
4848 udata.exclude = trk->trackpoints;
4850 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4852 // Note the limit to selecting one track only
4853 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4854 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4855 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4858 trk->is_route ? _("Append Track"): _("Append Route"),
4859 trk->is_route ? _("Select the track to append after the current route") :
4860 _("Select the route to append after the current track") );
4862 g_list_free(other_tracks_names);
4864 // It's a list, but shouldn't contain more than one other track!
4865 if ( append_list ) {
4867 for (l = append_list; l != NULL; l = g_list_next(l)) {
4868 // TODO: at present this uses the first track found by name,
4869 // which with potential multiple same named tracks may not be the one selected...
4871 // Get FROM THE OTHER TYPE list
4872 VikTrack *append_track;
4873 if ( trk->is_route )
4874 append_track = vik_trw_layer_get_track ( vtl, l->data );
4876 append_track = vik_trw_layer_get_route ( vtl, l->data );
4878 if ( append_track ) {
4880 if ( !append_track->is_route &&
4881 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
4882 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
4884 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4885 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
4886 vik_track_merge_segments ( append_track );
4887 vik_track_to_routepoints ( append_track );
4894 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4895 append_track->trackpoints = NULL;
4897 // Delete copied which is FROM THE OTHER TYPE list
4898 if ( trk->is_route )
4899 vik_trw_layer_delete_track (vtl, append_track);
4901 vik_trw_layer_delete_route (vtl, append_track);
4904 for (l = append_list; l != NULL; l = g_list_next(l))
4906 g_list_free(append_list);
4907 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4911 /* merge by segments */
4912 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4914 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4915 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4916 guint segments = vik_track_merge_segments ( trk );
4917 // NB currently no need to redraw as segments not actually shown on the display
4918 // However inform the user of what happened:
4920 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4921 g_snprintf(str, 64, tmp_str, segments);
4922 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4925 /* merge by time routine */
4926 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4928 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4932 GList *tracks_with_timestamp = NULL;
4933 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4934 if (orig_trk->trackpoints &&
4935 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4936 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4941 udata.result = &tracks_with_timestamp;
4942 udata.exclude = orig_trk->trackpoints;
4943 udata.with_timestamps = TRUE;
4944 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4945 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4947 if (!tracks_with_timestamp) {
4948 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4951 g_list_free(tracks_with_timestamp);
4953 static guint threshold_in_minutes = 1;
4954 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4955 _("Merge Threshold..."),
4956 _("Merge when time between tracks less than:"),
4957 &threshold_in_minutes)) {
4961 // keep attempting to merge all tracks until no merges within the time specified is possible
4962 gboolean attempt_merge = TRUE;
4963 GList *nearby_tracks = NULL;
4965 static gpointer params[3];
4967 while ( attempt_merge ) {
4969 // Don't try again unless tracks have changed
4970 attempt_merge = FALSE;
4972 trps = orig_trk->trackpoints;
4976 if (nearby_tracks) {
4977 g_list_free(nearby_tracks);
4978 nearby_tracks = NULL;
4981 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4982 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4984 /* g_print("Original track times: %d and %d\n", t1, t2); */
4985 params[0] = &nearby_tracks;
4986 params[1] = (gpointer)trps;
4987 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4989 /* get a list of adjacent-in-time tracks */
4990 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4993 GList *l = nearby_tracks;
4996 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4997 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4999 t1 = get_first_trackpoint(l)->timestamp;
5000 t2 = get_last_trackpoint(l)->timestamp;
5001 #undef get_first_trackpoint
5002 #undef get_last_trackpoint
5003 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5006 /* remove trackpoints from merged track, delete track */
5007 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
5008 VIK_TRACK(l->data)->trackpoints = NULL;
5009 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5011 // Tracks have changed, therefore retry again against all the remaining tracks
5012 attempt_merge = TRUE;
5017 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5020 g_list_free(nearby_tracks);
5021 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5025 * Split a track at the currently selected trackpoint
5027 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5029 if ( !vtl->current_tpl )
5032 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5033 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5035 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5036 GList *newglist = g_list_alloc ();
5037 newglist->prev = NULL;
5038 newglist->next = vtl->current_tpl->next;
5039 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5040 tr->trackpoints = newglist;
5042 vtl->current_tpl->next->prev = newglist; /* end old track here */
5043 vtl->current_tpl->next = NULL;
5045 vtl->current_tpl = newglist; /* change tp to first of new track. */
5046 vtl->current_tp_track = tr;
5049 vik_trw_layer_add_route ( vtl, name, tr );
5051 vik_trw_layer_add_track ( vtl, name, tr );
5057 // Also need id of newly created track
5060 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5062 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5064 if ( trkf && udata.uuid )
5065 vtl->current_tp_id = udata.uuid;
5067 vtl->current_tp_id = NULL;
5069 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5074 /* split by time routine */
5075 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5077 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5078 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5079 GList *trps = track->trackpoints;
5081 GList *newlists = NULL;
5082 GList *newtps = NULL;
5083 static guint thr = 1;
5090 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5091 _("Split Threshold..."),
5092 _("Split when time between trackpoints exceeds:"),
5097 /* iterate through trackpoints, and copy them into new lists without touching original list */
5098 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5102 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5104 g_print("panic: ts < prev_ts: this should never happen!\n");
5107 if (ts - prev_ts > thr*60) {
5108 /* flush accumulated trackpoints into new list */
5109 newlists = g_list_append(newlists, g_list_reverse(newtps));
5113 /* accumulate trackpoint copies in newtps, in reverse order */
5114 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5116 iter = g_list_next(iter);
5119 newlists = g_list_append(newlists, g_list_reverse(newtps));
5122 /* put lists of trackpoints into tracks */
5124 // Only bother updating if the split results in new tracks
5125 if (g_list_length (newlists) > 1) {
5130 tr = vik_track_copy ( track, FALSE );
5131 tr->trackpoints = (GList *)(iter->data);
5133 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5134 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5135 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5136 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5138 iter = g_list_next(iter);
5140 // Remove original track and then update the display
5141 vik_trw_layer_delete_track (vtl, track);
5142 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
5144 g_list_free(newlists);
5148 * Split a track by the number of points as specified by the user
5150 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5152 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5154 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5155 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5157 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5162 // Check valid track
5163 GList *trps = track->trackpoints;
5167 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5168 _("Split Every Nth Point"),
5169 _("Split on every Nth point:"),
5170 250, // Default value as per typical limited track capacity of various GPS devices
5174 // Was a valid number returned?
5180 GList *newlists = NULL;
5181 GList *newtps = NULL;
5186 /* accumulate trackpoint copies in newtps, in reverse order */
5187 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5189 if (count >= points) {
5190 /* flush accumulated trackpoints into new list */
5191 newlists = g_list_append(newlists, g_list_reverse(newtps));
5195 iter = g_list_next(iter);
5198 // If there is a remaining chunk put that into the new split list
5199 // This may well be the whole track if no split points were encountered
5201 newlists = g_list_append(newlists, g_list_reverse(newtps));
5204 /* put lists of trackpoints into tracks */
5206 // Only bother updating if the split results in new tracks
5207 if (g_list_length (newlists) > 1) {
5212 tr = vik_track_copy ( track, FALSE );
5213 tr->trackpoints = (GList *)(iter->data);
5215 if ( track->is_route ) {
5216 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5217 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5220 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5221 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5223 iter = g_list_next(iter);
5225 // Remove original track and then update the display
5226 if ( track->is_route )
5227 vik_trw_layer_delete_route (vtl, track);
5229 vik_trw_layer_delete_track (vtl, track);
5230 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
5232 g_list_free(newlists);
5236 * Split a track at the currently selected trackpoint
5238 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5240 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5241 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5242 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5246 * Split a track by its segments
5247 * Routes do not have segments so don't call this for routes
5249 static void trw_layer_split_segments ( gpointer pass_along[6] )
5251 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5252 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5259 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5262 for ( i = 0; i < ntracks; i++ ) {
5264 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5265 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5270 // Remove original track
5271 vik_trw_layer_delete_track ( vtl, trk );
5272 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5275 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5278 /* end of split/merge routines */
5281 * Delete adjacent track points at the same position
5282 * AKA Delete Dulplicates on the Properties Window
5284 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5286 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5288 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5289 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5291 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5296 gulong removed = vik_track_remove_dup_points ( trk );
5298 // Track has been updated so update tps:
5299 trw_layer_cancel_tps_of_track ( vtl, trk );
5301 // Inform user how much was deleted as it's not obvious from the normal view
5303 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5304 g_snprintf(str, 64, tmp_str, removed);
5305 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5307 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5311 * Delete adjacent track points with the same timestamp
5312 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5314 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5316 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5318 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5319 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5321 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5326 gulong removed = vik_track_remove_same_time_points ( trk );
5328 // Track has been updated so update tps:
5329 trw_layer_cancel_tps_of_track ( vtl, trk );
5331 // Inform user how much was deleted as it's not obvious from the normal view
5333 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5334 g_snprintf(str, 64, tmp_str, removed);
5335 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5337 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5343 static void trw_layer_reverse ( gpointer pass_along[6] )
5345 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5347 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5348 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5350 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5355 // Check valid track
5356 GList *trps = track->trackpoints;
5360 vik_track_reverse ( track );
5362 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
5366 * Similar to trw_layer_enum_item, but this uses a sorted method
5369 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5371 GList **list = (GList**)udata;
5372 // *list = g_list_prepend(*all, key); //unsorted method
5373 // Sort named list alphabetically
5374 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5379 * Now Waypoint specific sort
5381 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5383 GList **list = (GList**)udata;
5384 // Sort named list alphabetically
5385 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5389 * Track specific sort
5391 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5393 GList **list = (GList**)udata;
5394 // Sort named list alphabetically
5395 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5400 gboolean has_same_track_name;
5401 const gchar *same_track_name;
5402 } same_track_name_udata;
5404 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5406 const gchar* namea = (const gchar*) aa;
5407 const gchar* nameb = (const gchar*) bb;
5410 gint result = strcmp ( namea, nameb );
5412 if ( result == 0 ) {
5413 // Found two names the same
5414 same_track_name_udata *user_data = udata;
5415 user_data->has_same_track_name = TRUE;
5416 user_data->same_track_name = namea;
5419 // Leave ordering the same
5424 * Find out if any tracks have the same name in this hash table
5426 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5428 // Sort items by name, then compare if any next to each other are the same
5430 GList *track_names = NULL;
5431 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5434 if ( ! track_names )
5437 same_track_name_udata udata;
5438 udata.has_same_track_name = FALSE;
5440 // Use sort routine to traverse list comparing items
5441 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5442 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5443 // Still no tracks...
5447 return udata.has_same_track_name;
5451 * Force unqiue track names for the track table specified
5452 * Note the panel is a required parameter to enable the update of the names displayed
5453 * Specify if on tracks or else on routes
5455 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5457 // . Search list for an instance of repeated name
5458 // . get track of this name
5459 // . create new name
5460 // . rename track & update equiv. treeview iter
5461 // . repeat until all different
5463 same_track_name_udata udata;
5465 GList *track_names = NULL;
5466 udata.has_same_track_name = FALSE;
5467 udata.same_track_name = NULL;
5469 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5472 if ( ! track_names )
5475 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5477 // Still no tracks...
5478 if ( ! dummy_list1 )
5481 while ( udata.has_same_track_name ) {
5483 // Find a track with the same name
5486 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5488 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5492 g_critical("Houston, we've had a problem.");
5493 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5494 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5499 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5500 vik_track_set_name ( trk, newname );
5506 // Need want key of it for treeview update
5507 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5509 if ( trkf && udataU.uuid ) {
5513 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5515 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5518 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5519 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5520 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5525 // Start trying to find same names again...
5527 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5528 udata.has_same_track_name = FALSE;
5529 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5531 // No tracks any more - give up searching
5532 if ( ! dummy_list2 )
5533 udata.has_same_track_name = FALSE;
5537 vik_layers_panel_emit_update ( vlp );
5543 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5545 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5548 // Ensure list of track names offered is unique
5549 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5550 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5551 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5552 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5558 // Sort list alphabetically for better presentation
5559 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5562 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5566 // Get list of items to delete from the user
5567 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5570 _("Delete Selection"),
5571 _("Select tracks to delete"));
5574 // Delete requested tracks
5575 // since specificly requested, IMHO no need for extra confirmation
5576 if ( delete_list ) {
5578 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5579 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5580 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5582 g_list_free(delete_list);
5583 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5590 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5592 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5595 // Ensure list of track names offered is unique
5596 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5597 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5598 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5599 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5605 // Sort list alphabetically for better presentation
5606 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5609 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5613 // Get list of items to delete from the user
5614 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5617 _("Delete Selection"),
5618 _("Select routes to delete") );
5621 // Delete requested routes
5622 // since specificly requested, IMHO no need for extra confirmation
5623 if ( delete_list ) {
5625 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5626 // This deletes first route it finds of that name (but uniqueness is enforced above)
5627 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5629 g_list_free(delete_list);
5630 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5635 gboolean has_same_waypoint_name;
5636 const gchar *same_waypoint_name;
5637 } same_waypoint_name_udata;
5639 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5641 const gchar* namea = (const gchar*) aa;
5642 const gchar* nameb = (const gchar*) bb;
5645 gint result = strcmp ( namea, nameb );
5647 if ( result == 0 ) {
5648 // Found two names the same
5649 same_waypoint_name_udata *user_data = udata;
5650 user_data->has_same_waypoint_name = TRUE;
5651 user_data->same_waypoint_name = namea;
5654 // Leave ordering the same
5659 * Find out if any waypoints have the same name in this layer
5661 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5663 // Sort items by name, then compare if any next to each other are the same
5665 GList *waypoint_names = NULL;
5666 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5669 if ( ! waypoint_names )
5672 same_waypoint_name_udata udata;
5673 udata.has_same_waypoint_name = FALSE;
5675 // Use sort routine to traverse list comparing items
5676 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5677 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5678 // Still no waypoints...
5682 return udata.has_same_waypoint_name;
5686 * Force unqiue waypoint names for this layer
5687 * Note the panel is a required parameter to enable the update of the names displayed
5689 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5691 // . Search list for an instance of repeated name
5692 // . get waypoint of this name
5693 // . create new name
5694 // . rename waypoint & update equiv. treeview iter
5695 // . repeat until all different
5697 same_waypoint_name_udata udata;
5699 GList *waypoint_names = NULL;
5700 udata.has_same_waypoint_name = FALSE;
5701 udata.same_waypoint_name = NULL;
5703 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5706 if ( ! waypoint_names )
5709 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5711 // Still no waypoints...
5712 if ( ! dummy_list1 )
5715 while ( udata.has_same_waypoint_name ) {
5717 // Find a waypoint with the same name
5718 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5722 g_critical("Houston, we've had a problem.");
5723 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5724 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5729 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5730 vik_waypoint_set_name ( waypoint, newname );
5733 udataU.wp = waypoint;
5736 // Need want key of it for treeview update
5737 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5739 if ( wpf && udataU.uuid ) {
5741 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5744 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5745 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5746 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5751 // Start trying to find same names again...
5752 waypoint_names = NULL;
5753 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5754 udata.has_same_waypoint_name = FALSE;
5755 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5757 // No waypoints any more - give up searching
5758 if ( ! dummy_list2 )
5759 udata.has_same_waypoint_name = FALSE;
5763 vik_layers_panel_emit_update ( vlp );
5769 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5771 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5774 // Ensure list of waypoint names offered is unique
5775 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5776 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5777 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5778 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5784 // Sort list alphabetically for better presentation
5785 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5787 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5791 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5793 // Get list of items to delete from the user
5794 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5797 _("Delete Selection"),
5798 _("Select waypoints to delete"));
5801 // Delete requested waypoints
5802 // since specificly requested, IMHO no need for extra confirmation
5803 if ( delete_list ) {
5805 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5806 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5807 trw_layer_delete_waypoint_by_name (vtl, l->data);
5809 g_list_free(delete_list);
5810 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5815 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5817 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5819 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5822 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5824 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5827 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
5828 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5832 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
5834 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5837 if ( !strncmp(wp->comment, "http", 4) ) {
5838 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
5839 } else if ( !strncmp(wp->description, "http", 4) ) {
5840 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
5844 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
5846 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5848 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
5850 // No actual change to the name supplied
5851 if (strcmp(newname, wp->name) == 0 )
5854 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5857 // An existing waypoint has been found with the requested name
5858 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5859 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
5864 // Update WP name and refresh the treeview
5865 vik_waypoint_set_name (wp, newname);
5867 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5868 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5871 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5876 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5878 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
5880 // No actual change to the name supplied
5881 if (strcmp(newname, trk->name) == 0)
5884 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
5887 // An existing track has been found with the requested name
5888 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5889 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
5893 // Update track name and refresh GUI parts
5894 vik_track_set_name (trk, newname);
5896 // Update any subwindows that could be displaying this track which has changed name
5897 // Only one Track Edit Window
5898 if ( l->current_tp_track == trk && l->tpwin ) {
5899 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5901 // Property Dialog of the track
5902 vik_trw_layer_propwin_update ( trk );
5904 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5905 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5908 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5913 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5915 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
5917 // No actual change to the name supplied
5918 if (strcmp(newname, trk->name) == 0)
5921 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
5924 // An existing track has been found with the requested name
5925 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5926 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
5930 // Update track name and refresh GUI parts
5931 vik_track_set_name (trk, newname);
5933 // Update any subwindows that could be displaying this track which has changed name
5934 // Only one Track Edit Window
5935 if ( l->current_tp_track == trk && l->tpwin ) {
5936 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5938 // Property Dialog of the track
5939 vik_trw_layer_propwin_update ( trk );
5941 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5942 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5945 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5952 static gboolean is_valid_geocache_name ( gchar *str )
5954 gint len = strlen ( str );
5955 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]));
5958 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
5960 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5961 a_acquire_set_filter_track ( trk );
5964 #ifdef VIK_CONFIG_GOOGLE
5965 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
5967 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
5968 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
5971 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
5973 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
5975 gchar *escaped = uri_escape ( tr->comment );
5976 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
5977 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5984 /* vlp can be NULL if necessary - i.e. right-click from a tool */
5985 /* viewpoint is now available instead */
5986 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
5988 static gpointer pass_along[8];
5990 gboolean rv = FALSE;
5993 pass_along[1] = vlp;
5994 pass_along[2] = GINT_TO_POINTER (subtype);
5995 pass_along[3] = sublayer;
5996 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5997 pass_along[5] = vvp;
5998 pass_along[6] = iter;
5999 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6001 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6005 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6006 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6007 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6008 gtk_widget_show ( item );
6010 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6011 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6012 if (tr && tr->property_dialog)
6013 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6015 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6016 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6017 if (tr && tr->property_dialog)
6018 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6021 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6023 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6024 gtk_widget_show ( item );
6026 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6028 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6029 gtk_widget_show ( item );
6031 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6033 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6034 gtk_widget_show ( item );
6036 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6038 gboolean separator_created = FALSE;
6040 /* could be a right-click using the tool */
6041 if ( vlp != NULL ) {
6042 item = gtk_menu_item_new ();
6043 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6044 gtk_widget_show ( item );
6046 separator_created = TRUE;
6048 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6049 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6051 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6052 gtk_widget_show ( item );
6055 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6057 if ( wp && wp->name ) {
6058 if ( is_valid_geocache_name ( wp->name ) ) {
6060 if ( !separator_created ) {
6061 item = gtk_menu_item_new ();
6062 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6063 gtk_widget_show ( item );
6064 separator_created = TRUE;
6067 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6069 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6070 gtk_widget_show ( item );
6074 if ( wp && wp->image )
6076 if ( !separator_created ) {
6077 item = gtk_menu_item_new ();
6078 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6079 gtk_widget_show ( item );
6080 separator_created = TRUE;
6083 // Set up image paramater
6084 pass_along[5] = wp->image;
6086 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6087 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
6088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6089 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6090 gtk_widget_show ( item );
6092 #ifdef VIK_CONFIG_GEOTAG
6093 GtkWidget *geotag_submenu = gtk_menu_new ();
6094 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6095 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6096 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6097 gtk_widget_show ( item );
6098 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6100 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6101 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6102 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6103 gtk_widget_show ( item );
6105 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6106 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6107 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6108 gtk_widget_show ( item );
6114 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6115 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6116 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6117 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6118 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6119 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6120 gtk_widget_show ( item );
6127 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6128 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6129 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6130 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6131 gtk_widget_show ( item );
6132 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6133 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6134 gtk_widget_set_sensitive ( item, TRUE );
6136 gtk_widget_set_sensitive ( item, FALSE );
6139 item = gtk_menu_item_new ();
6140 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6141 gtk_widget_show ( item );
6144 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6147 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6148 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6149 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6150 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6151 gtk_widget_show ( item );
6154 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6156 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6159 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6160 gtk_widget_show ( item );
6162 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6165 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6166 gtk_widget_show ( item );
6168 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6171 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6172 gtk_widget_show ( item );
6174 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6177 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6178 gtk_widget_show ( item );
6181 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6185 if ( l->current_track && !l->current_track->is_route ) {
6186 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6187 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6188 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6189 gtk_widget_show ( item );
6191 item = gtk_menu_item_new ();
6192 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6193 gtk_widget_show ( item );
6196 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6197 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6198 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6199 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6200 gtk_widget_show ( item );
6202 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6203 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6205 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6206 gtk_widget_show ( item );
6207 // Make it available only when a new track *not* already in progress
6208 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6210 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6212 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6213 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6214 gtk_widget_show ( item );
6216 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6217 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6219 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6220 gtk_widget_show ( item );
6223 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6227 if ( l->current_track && l->current_track->is_route ) {
6228 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6229 // Reuse finish track method
6230 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6231 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6232 gtk_widget_show ( item );
6234 item = gtk_menu_item_new ();
6235 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6236 gtk_widget_show ( item );
6239 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6240 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6241 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6242 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6243 gtk_widget_show ( item );
6245 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6246 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6247 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6248 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6249 gtk_widget_show ( item );
6250 // Make it available only when a new track *not* already in progress
6251 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6253 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6254 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6257 gtk_widget_show ( item );
6259 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6260 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6261 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6262 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6263 gtk_widget_show ( item );
6266 GtkWidget *upload_submenu = gtk_menu_new ();
6268 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6270 item = gtk_menu_item_new ();
6271 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6272 gtk_widget_show ( item );
6274 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6275 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6276 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6277 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6278 if ( l->current_track ) {
6279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6280 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6281 gtk_widget_show ( item );
6284 item = gtk_menu_item_new ();
6285 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6286 gtk_widget_show ( item );
6289 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6290 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6292 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6293 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6294 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6295 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6296 gtk_widget_show ( item );
6298 GtkWidget *goto_submenu;
6299 goto_submenu = gtk_menu_new ();
6300 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6301 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6302 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6303 gtk_widget_show ( item );
6304 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6306 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6307 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6308 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6309 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6310 gtk_widget_show ( item );
6312 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6313 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6314 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6315 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6316 gtk_widget_show ( item );
6318 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6319 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6320 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6321 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6322 gtk_widget_show ( item );
6324 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6325 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6326 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6327 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6328 gtk_widget_show ( item );
6330 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6331 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6332 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6333 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6334 gtk_widget_show ( item );
6336 // Routes don't have speeds
6337 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6338 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6339 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6340 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6341 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6342 gtk_widget_show ( item );
6345 GtkWidget *combine_submenu;
6346 combine_submenu = gtk_menu_new ();
6347 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6348 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6349 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6350 gtk_widget_show ( item );
6351 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6353 // Routes don't have times or segments...
6354 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6355 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6356 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6357 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6358 gtk_widget_show ( item );
6360 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6361 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6362 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6363 gtk_widget_show ( item );
6366 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6368 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6369 gtk_widget_show ( item );
6371 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6372 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6374 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6376 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6377 gtk_widget_show ( item );
6379 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6380 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6382 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6383 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6384 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6385 gtk_widget_show ( item );
6387 GtkWidget *split_submenu;
6388 split_submenu = gtk_menu_new ();
6389 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6390 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6391 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6392 gtk_widget_show ( item );
6393 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6395 // Routes don't have times or segments...
6396 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6397 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6398 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6399 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6400 gtk_widget_show ( item );
6402 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6403 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6404 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6405 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6406 gtk_widget_show ( item );
6409 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6410 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6411 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6412 gtk_widget_show ( item );
6414 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6415 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6416 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6417 gtk_widget_show ( item );
6418 // Make it available only when a trackpoint is selected.
6419 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6421 GtkWidget *delete_submenu;
6422 delete_submenu = gtk_menu_new ();
6423 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6424 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6425 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6426 gtk_widget_show ( item );
6427 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6429 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6430 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6431 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6432 gtk_widget_show ( item );
6434 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6435 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6436 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6437 gtk_widget_show ( item );
6439 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6440 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6442 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6443 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6444 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6445 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6446 gtk_widget_show ( item );
6448 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6450 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6451 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6453 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6454 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
6455 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6456 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6457 gtk_widget_show ( item );
6460 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6461 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
6462 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6463 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6464 gtk_widget_show ( item );
6466 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6467 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6469 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6470 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6471 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6472 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6473 gtk_widget_show ( item );
6475 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6476 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6478 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6479 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6480 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6481 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6482 gtk_widget_show ( item );
6484 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6485 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6487 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6488 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6490 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6491 gtk_widget_show ( item );
6493 #ifdef VIK_CONFIG_GOOGLE
6494 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6495 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6496 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
6497 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6498 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6499 gtk_widget_show ( item );
6503 // ATM can't upload a single waypoint but can do waypoints to a GPS
6504 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6505 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6506 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6507 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6508 gtk_widget_show ( item );
6509 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6511 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6512 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6513 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6514 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6515 gtk_widget_show ( item );
6519 #ifdef VIK_CONFIG_GOOGLE
6520 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6522 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6523 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6524 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6525 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6526 gtk_widget_show ( item );
6530 // Some things aren't usable with routes
6531 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6532 #ifdef VIK_CONFIG_OPENSTREETMAP
6533 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6534 // Convert internal pointer into actual track for usage outside this file
6535 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6536 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6537 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6538 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6539 gtk_widget_show ( item );
6542 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6543 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6544 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6545 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6546 gtk_widget_show ( item );
6548 /* ATM This function is only available via the layers panel, due to needing a vlp */
6550 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6551 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6552 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6554 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6555 gtk_widget_show ( item );
6559 #ifdef VIK_CONFIG_GEOTAG
6560 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6562 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6563 gtk_widget_show ( item );
6567 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6568 // Only show on viewport popmenu when a trackpoint is selected
6569 if ( ! vlp && l->current_tpl ) {
6571 item = gtk_menu_item_new ();
6572 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6573 gtk_widget_show ( item );
6575 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6576 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6577 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6578 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6579 gtk_widget_show ( item );
6586 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6589 if (!vtl->current_tpl)
6591 if (!vtl->current_tpl->next)
6594 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6595 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6597 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6600 VikTrackpoint *tp_new = vik_trackpoint_new();
6601 struct LatLon ll_current, ll_next;
6602 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6603 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6605 /* main positional interpolation */
6606 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6607 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6609 /* Now other properties that can be interpolated */
6610 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6612 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6613 /* Note here the division is applied to each part, then added
6614 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6615 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6616 tp_new->has_timestamp = TRUE;
6619 if (tp_current->speed != NAN && tp_next->speed != NAN)
6620 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6622 /* TODO - improve interpolation of course, as it may not be correct.
6623 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6624 [similar applies if value is in radians] */
6625 if (tp_current->course != NAN && tp_next->course != NAN)
6626 tp_new->speed = (tp_current->course + tp_next->course)/2;
6628 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6630 /* Insert new point into the trackpoints list after the current TP */
6631 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6633 // Otherwise try routes
6634 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6638 gint index = g_list_index ( trk->trackpoints, tp_current );
6640 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6645 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6651 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6655 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6657 if ( vtl->current_tpl )
6659 vtl->current_tpl = NULL;
6660 vtl->current_tp_track = NULL;
6661 vtl->current_tp_id = NULL;
6662 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6666 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6668 g_assert ( vtl->tpwin != NULL );
6669 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6670 trw_layer_cancel_current_tp ( vtl, TRUE );
6672 if ( vtl->current_tpl == NULL )
6675 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6677 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6678 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6680 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6682 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6684 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6690 // Find available adjacent trackpoint
6691 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6693 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6694 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6696 // Delete current trackpoint
6697 vik_trackpoint_free ( vtl->current_tpl->data );
6698 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6700 // Set to current to the available adjacent trackpoint
6701 vtl->current_tpl = new_tpl;
6703 // Reset dialog with the available adjacent trackpoint
6704 if ( vtl->current_tp_track )
6705 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6707 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6711 // Delete current trackpoint
6712 vik_trackpoint_free ( vtl->current_tpl->data );
6713 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6714 trw_layer_cancel_current_tp ( vtl, FALSE );
6717 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6719 if ( vtl->current_tp_track )
6720 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6721 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
6723 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6725 if ( vtl->current_tp_track )
6726 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6727 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6729 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6731 trw_layer_insert_tp_after_current_tp ( vtl );
6732 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6734 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6735 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6738 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
6742 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6743 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
6744 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
6745 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
6746 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
6748 if ( vtl->current_tpl )
6749 if ( vtl->current_tp_track )
6750 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6751 /* set layer name and TP data */
6754 /***************************************************************************
6756 ***************************************************************************/
6758 /*** Utility data structures and functions ****/
6762 gint closest_x, closest_y;
6763 gpointer *closest_wp_id;
6764 VikWaypoint *closest_wp;
6770 gint closest_x, closest_y;
6771 gpointer closest_track_id;
6772 VikTrackpoint *closest_tp;
6777 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
6783 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
6785 // If waypoint has an image then use the image size to select
6787 gint slackx, slacky;
6788 slackx = wp->image_width / 2;
6789 slacky = wp->image_height / 2;
6791 if ( x <= params->x + slackx && x >= params->x - slackx
6792 && y <= params->y + slacky && y >= params->y - slacky ) {
6793 params->closest_wp_id = id;
6794 params->closest_wp = wp;
6795 params->closest_x = x;
6796 params->closest_y = y;
6799 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
6800 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
6801 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6803 params->closest_wp_id = id;
6804 params->closest_wp = wp;
6805 params->closest_x = x;
6806 params->closest_y = y;
6810 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
6812 GList *tpl = t->trackpoints;
6821 tp = VIK_TRACKPOINT(tpl->data);
6823 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
6825 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
6826 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
6827 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6829 params->closest_track_id = id;
6830 params->closest_tp = tp;
6831 params->closest_tpl = tpl;
6832 params->closest_x = x;
6833 params->closest_y = y;
6839 // ATM: Leave this as 'Track' only.
6840 // Not overly bothered about having a snap to route trackpoint capability
6841 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6843 TPSearchParams params;
6847 params.closest_track_id = NULL;
6848 params.closest_tp = NULL;
6849 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6850 return params.closest_tp;
6853 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6855 WPSearchParams params;
6859 params.closest_wp = NULL;
6860 params.closest_wp_id = NULL;
6861 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6862 return params.closest_wp;
6866 // Some forward declarations
6867 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
6868 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
6869 static void marker_end_move ( tool_ed_t *t );
6872 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6876 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6878 // Here always allow snapping back to the original location
6879 // this is useful when one decides not to move the thing afterall
6880 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
6883 if ( event->state & GDK_CONTROL_MASK )
6885 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6887 new_coord = tp->coord;
6891 if ( event->state & GDK_SHIFT_MASK )
6893 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6895 new_coord = wp->coord;
6899 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6901 marker_moveto ( t, x, y );
6908 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6910 if ( t->holding && event->button == 1 )
6913 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6916 if ( event->state & GDK_CONTROL_MASK )
6918 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6920 new_coord = tp->coord;
6924 if ( event->state & GDK_SHIFT_MASK )
6926 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6928 new_coord = wp->coord;
6931 marker_end_move ( t );
6933 // Determine if working on a waypoint or a trackpoint
6934 if ( t->is_waypoint )
6935 vtl->current_wp->coord = new_coord;
6937 if ( vtl->current_tpl ) {
6938 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6941 if ( vtl->current_tp_track )
6942 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6947 vtl->current_wp = NULL;
6948 vtl->current_wp_id = NULL;
6949 trw_layer_cancel_current_tp ( vtl, FALSE );
6951 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6958 Returns true if a waypoint or track is found near the requested event position for this particular layer
6959 The item found is automatically selected
6960 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
6962 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
6964 if ( event->button != 1 )
6967 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6970 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
6973 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
6975 if (vtl->waypoints_visible) {
6976 WPSearchParams wp_params;
6977 wp_params.vvp = vvp;
6978 wp_params.x = event->x;
6979 wp_params.y = event->y;
6980 wp_params.closest_wp_id = NULL;
6981 wp_params.closest_wp = NULL;
6983 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
6985 if ( wp_params.closest_wp ) {
6988 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
6990 // Too easy to move it so must be holding shift to start immediately moving it
6991 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
6992 if ( event->state & GDK_SHIFT_MASK ||
6993 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
6994 // Put into 'move buffer'
6995 // NB vvp & vw already set in tet
6996 tet->vtl = (gpointer)vtl;
6997 tet->is_waypoint = TRUE;
6999 marker_begin_move (tet, event->x, event->y);
7002 vtl->current_wp = wp_params.closest_wp;
7003 vtl->current_wp_id = wp_params.closest_wp_id;
7005 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7011 // Used for both track and route lists
7012 TPSearchParams tp_params;
7013 tp_params.vvp = vvp;
7014 tp_params.x = event->x;
7015 tp_params.y = event->y;
7016 tp_params.closest_track_id = NULL;
7017 tp_params.closest_tp = NULL;
7019 if (vtl->tracks_visible) {
7020 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7022 if ( tp_params.closest_tp ) {
7024 // Always select + highlight the track
7025 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7027 tet->is_waypoint = FALSE;
7029 // Select the Trackpoint
7030 // Can move it immediately when control held or it's the previously selected tp
7031 if ( event->state & GDK_CONTROL_MASK ||
7032 vtl->current_tpl == tp_params.closest_tpl ) {
7033 // Put into 'move buffer'
7034 // NB vvp & vw already set in tet
7035 tet->vtl = (gpointer)vtl;
7036 marker_begin_move (tet, event->x, event->y);
7039 vtl->current_tpl = tp_params.closest_tpl;
7040 vtl->current_tp_id = tp_params.closest_track_id;
7041 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7043 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7046 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7048 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7053 // Try again for routes
7054 if (vtl->routes_visible) {
7055 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7057 if ( tp_params.closest_tp ) {
7059 // Always select + highlight the track
7060 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7062 tet->is_waypoint = FALSE;
7064 // Select the Trackpoint
7065 // Can move it immediately when control held or it's the previously selected tp
7066 if ( event->state & GDK_CONTROL_MASK ||
7067 vtl->current_tpl == tp_params.closest_tpl ) {
7068 // Put into 'move buffer'
7069 // NB vvp & vw already set in tet
7070 tet->vtl = (gpointer)vtl;
7071 marker_begin_move (tet, event->x, event->y);
7074 vtl->current_tpl = tp_params.closest_tpl;
7075 vtl->current_tp_id = tp_params.closest_track_id;
7076 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7078 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7081 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7083 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7088 /* these aren't the droids you're looking for */
7089 vtl->current_wp = NULL;
7090 vtl->current_wp_id = NULL;
7091 trw_layer_cancel_current_tp ( vtl, FALSE );
7094 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7099 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7101 if ( event->button != 3 )
7104 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7107 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7110 /* Post menu for the currently selected item */
7112 /* See if a track is selected */
7113 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7114 if ( track && track->visible ) {
7116 if ( track->name ) {
7118 if ( vtl->track_right_click_menu )
7119 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
7121 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7128 if ( track->is_route )
7129 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7131 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7133 if ( trkf && udataU.uuid ) {
7136 if ( track->is_route )
7137 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7139 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7141 trw_layer_sublayer_add_menu_items ( vtl,
7142 vtl->track_right_click_menu,
7144 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7150 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7156 /* See if a waypoint is selected */
7157 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7158 if ( waypoint && waypoint->visible ) {
7159 if ( waypoint->name ) {
7161 if ( vtl->wp_right_click_menu )
7162 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
7164 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7167 udata.wp = waypoint;
7170 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7172 if ( wpf && udata.uuid ) {
7173 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7175 trw_layer_sublayer_add_menu_items ( vtl,
7176 vtl->wp_right_click_menu,
7178 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7183 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7192 /* background drawing hook, to be passed the viewport */
7193 static gboolean tool_sync_done = TRUE;
7195 static gboolean tool_sync(gpointer data)
7197 VikViewport *vvp = data;
7198 gdk_threads_enter();
7199 vik_viewport_sync(vvp);
7200 tool_sync_done = TRUE;
7201 gdk_threads_leave();
7205 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7208 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7209 gdk_gc_set_function ( t->gc, GDK_INVERT );
7210 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7211 vik_viewport_sync(t->vvp);
7216 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7218 VikViewport *vvp = t->vvp;
7219 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7220 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7224 if (tool_sync_done) {
7225 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7226 tool_sync_done = FALSE;
7230 static void marker_end_move ( tool_ed_t *t )
7232 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7233 g_object_unref ( t->gc );
7237 /*** Edit waypoint ****/
7239 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7241 tool_ed_t *t = g_new(tool_ed_t, 1);
7247 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7249 WPSearchParams params;
7250 tool_ed_t *t = data;
7251 VikViewport *vvp = t->vvp;
7253 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7260 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7263 if ( vtl->current_wp && vtl->current_wp->visible )
7265 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7267 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7269 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7270 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7272 if ( event->button == 3 )
7273 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7275 marker_begin_move(t, event->x, event->y);
7282 params.x = event->x;
7283 params.y = event->y;
7284 params.closest_wp_id = NULL;
7285 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7286 params.closest_wp = NULL;
7287 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7288 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7290 // how do we get here?
7291 marker_begin_move(t, event->x, event->y);
7292 g_critical("shouldn't be here");
7295 else if ( params.closest_wp )
7297 if ( event->button == 3 )
7298 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7300 vtl->waypoint_rightclick = FALSE;
7302 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7304 vtl->current_wp = params.closest_wp;
7305 vtl->current_wp_id = params.closest_wp_id;
7307 /* could make it so don't update if old WP is off screen and new is null but oh well */
7308 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7312 vtl->current_wp = NULL;
7313 vtl->current_wp_id = NULL;
7314 vtl->waypoint_rightclick = FALSE;
7315 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7319 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7321 tool_ed_t *t = data;
7322 VikViewport *vvp = t->vvp;
7324 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7329 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7332 if ( event->state & GDK_CONTROL_MASK )
7334 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7336 new_coord = tp->coord;
7340 if ( event->state & GDK_SHIFT_MASK )
7342 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7343 if ( wp && wp != vtl->current_wp )
7344 new_coord = wp->coord;
7349 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7351 marker_moveto ( t, x, y );
7358 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7360 tool_ed_t *t = data;
7361 VikViewport *vvp = t->vvp;
7363 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7366 if ( t->holding && event->button == 1 )
7369 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7372 if ( event->state & GDK_CONTROL_MASK )
7374 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7376 new_coord = tp->coord;
7380 if ( event->state & GDK_SHIFT_MASK )
7382 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7383 if ( wp && wp != vtl->current_wp )
7384 new_coord = wp->coord;
7387 marker_end_move ( t );
7389 vtl->current_wp->coord = new_coord;
7390 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7393 /* PUT IN RIGHT PLACE!!! */
7394 if ( event->button == 3 && vtl->waypoint_rightclick )
7396 if ( vtl->wp_right_click_menu )
7397 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7398 if ( vtl->current_wp ) {
7399 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7400 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 );
7401 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7403 vtl->waypoint_rightclick = FALSE;
7408 /*** New track ****/
7410 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7417 GdkDrawable *drawable;
7423 * Draw specified pixmap
7425 static gboolean draw_sync ( gpointer data )
7427 draw_sync_t *ds = (draw_sync_t*) data;
7428 // Sometimes don't want to draw
7429 // normally because another update has taken precedent such as panning the display
7430 // which means this pixmap is no longer valid
7431 if ( ds->vtl->draw_sync_do ) {
7432 gdk_threads_enter();
7433 gdk_draw_drawable (ds->drawable,
7436 0, 0, 0, 0, -1, -1);
7437 ds->vtl->draw_sync_done = TRUE;
7438 gdk_threads_leave();
7443 static gchar* distance_string (gdouble distance)
7447 /* draw label with distance */
7448 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7449 switch (dist_units) {
7450 case VIK_UNITS_DISTANCE_MILES:
7451 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7452 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7453 } else if (distance < 1609.4) {
7454 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7456 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7460 // VIK_UNITS_DISTANCE_KILOMETRES
7461 if (distance >= 1000 && distance < 100000) {
7462 g_sprintf(str, "%3.2f km", distance/1000.0);
7463 } else if (distance < 1000) {
7464 g_sprintf(str, "%d m", (int)distance);
7466 g_sprintf(str, "%d km", (int)distance/1000);
7470 return g_strdup (str);
7474 * Actually set the message in statusbar
7476 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
7478 // Only show elevation data when track has some elevation properties
7479 gchar str_gain_loss[64];
7480 str_gain_loss[0] = '\0';
7482 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7483 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7484 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7486 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7489 // Write with full gain/loss information
7490 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
7491 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
7496 * Figure out what information should be set in the statusbar and then write it
7498 static void update_statusbar ( VikTrwLayer *vtl )
7500 // Get elevation data
7501 gdouble elev_gain, elev_loss;
7502 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7504 /* Find out actual distance of current track */
7505 gdouble distance = vik_track_get_length (vtl->current_track);
7506 gchar *str = distance_string (distance);
7508 statusbar_write (str, elev_gain, elev_loss, vtl);
7514 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7516 /* if we haven't sync'ed yet, we don't have time to do more. */
7517 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7518 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7519 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7521 static GdkPixmap *pixmap = NULL;
7523 // Need to check in case window has been resized
7524 w1 = vik_viewport_get_width(vvp);
7525 h1 = vik_viewport_get_height(vvp);
7527 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7529 gdk_drawable_get_size (pixmap, &w2, &h2);
7530 if (w1 != w2 || h1 != h2) {
7531 g_object_unref ( G_OBJECT ( pixmap ) );
7532 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7535 // Reset to background
7536 gdk_draw_drawable (pixmap,
7537 vtl->current_track_newpoint_gc,
7538 vik_viewport_get_pixmap(vvp),
7539 0, 0, 0, 0, -1, -1);
7541 draw_sync_t *passalong;
7544 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7546 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7547 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7548 // thus when we come to reset to the background it would include what we have already drawn!!
7549 gdk_draw_line ( pixmap,
7550 vtl->current_track_newpoint_gc,
7551 x1, y1, event->x, event->y );
7552 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7554 /* Find out actual distance of current track */
7555 gdouble distance = vik_track_get_length (vtl->current_track);
7557 // Now add distance to where the pointer is //
7560 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7561 vik_coord_to_latlon ( &coord, &ll );
7562 distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
7564 // Get elevation data
7565 gdouble elev_gain, elev_loss;
7566 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7568 // Adjust elevation data (if available) for the current pointer position
7570 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7571 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7572 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7573 // Adjust elevation of last track point
7574 if ( elev_new > last_tpt->altitude )
7576 elev_gain += elev_new - last_tpt->altitude;
7579 elev_loss += last_tpt->altitude - elev_new;
7583 gchar *str = distance_string (distance);
7585 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7586 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
7588 pango_layout_set_text (pl, str, -1);
7590 pango_layout_get_pixel_size ( pl, &wd, &hd );
7593 // offset from cursor a bit depending on font size
7597 // Create a background block to make the text easier to read over the background map
7598 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7599 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7600 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7602 g_object_unref ( G_OBJECT ( pl ) );
7603 g_object_unref ( G_OBJECT ( background_block_gc ) );
7605 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7606 passalong->vtl = vtl;
7607 passalong->pixmap = pixmap;
7608 passalong->drawable = GTK_WIDGET(vvp)->window;
7609 passalong->gc = vtl->current_track_newpoint_gc;
7611 // Update statusbar with full gain/loss information
7612 statusbar_write (str, elev_gain, elev_loss, vtl);
7616 // draw pixmap when we have time to
7617 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7618 vtl->draw_sync_done = FALSE;
7619 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7621 return VIK_LAYER_TOOL_ACK;
7624 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7626 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7627 vtl->current_track = NULL;
7628 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7630 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7632 if ( vtl->current_track->trackpoints )
7634 GList *last = g_list_last(vtl->current_track->trackpoints);
7635 g_free ( last->data );
7636 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7639 update_statusbar ( vtl );
7641 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7648 * Common function to handle trackpoint button requests on either a route or a track
7649 * . enables adding a point via normal click
7650 * . enables removal of last point via right click
7651 * . finishing of the track or route via double clicking
7653 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7657 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7660 if ( event->button == 2 ) {
7661 // As the display is panning, the new track pixmap is now invalid so don't draw it
7662 // otherwise this drawing done results in flickering back to an old image
7663 vtl->draw_sync_do = FALSE;
7667 if ( event->button == 3 )
7669 if ( !vtl->current_track )
7672 if ( vtl->current_track->trackpoints )
7674 GList *last = g_list_last(vtl->current_track->trackpoints);
7675 g_free ( last->data );
7676 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7678 update_statusbar ( vtl );
7680 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7684 if ( event->type == GDK_2BUTTON_PRESS )
7686 /* subtract last (duplicate from double click) tp then end */
7687 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7689 GList *last = g_list_last(vtl->current_track->trackpoints);
7690 g_free ( last->data );
7691 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7692 /* undo last, then end */
7693 vtl->current_track = NULL;
7695 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7699 tp = vik_trackpoint_new();
7700 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
7702 /* snap to other TP */
7703 if ( event->state & GDK_CONTROL_MASK )
7705 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7707 tp->coord = other_tp->coord;
7710 tp->newsegment = FALSE;
7711 tp->has_timestamp = FALSE;
7714 if ( vtl->current_track ) {
7715 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
7716 /* Auto attempt to get elevation from DEM data (if it's available) */
7717 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
7720 vtl->ct_x1 = vtl->ct_x2;
7721 vtl->ct_y1 = vtl->ct_y2;
7722 vtl->ct_x2 = event->x;
7723 vtl->ct_y2 = event->y;
7725 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7729 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7731 // ----------------------------------------------------- if current is a route - switch to new track
7732 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
7734 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
7735 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
7737 vtl->current_track = vik_track_new();
7738 vtl->current_track->visible = TRUE;
7739 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
7744 return tool_new_track_or_route_click ( vtl, event, vvp );
7747 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7749 if ( event->button == 2 ) {
7750 // Pan moving ended - enable potential point drawing again
7751 vtl->draw_sync_do = TRUE;
7752 vtl->draw_sync_done = TRUE;
7756 /*** New route ****/
7758 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
7763 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7765 // -------------------------- if current is a track - switch to new route
7766 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
7768 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
7769 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
7770 new_route_create_common ( vtl, name );
7774 return tool_new_track_or_route_click ( vtl, event, vvp );
7777 /*** New waypoint ****/
7779 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7784 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7787 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7789 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
7790 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
7791 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7796 /*** Edit trackpoint ****/
7798 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
7800 tool_ed_t *t = g_new(tool_ed_t, 1);
7806 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7808 tool_ed_t *t = data;
7809 VikViewport *vvp = t->vvp;
7810 TPSearchParams params;
7811 /* OUTDATED DOCUMENTATION:
7812 find 5 pixel range on each side. then put these UTM, and a pointer
7813 to the winning track name (and maybe the winning track itself), and a
7814 pointer to the winning trackpoint, inside an array or struct. pass
7815 this along, do a foreach on the tracks which will do a foreach on the
7818 params.x = event->x;
7819 params.y = event->y;
7820 params.closest_track_id = NULL;
7821 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7822 params.closest_tp = NULL;
7824 if ( event->button != 1 )
7827 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7830 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
7833 if ( vtl->current_tpl )
7835 /* 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.) */
7836 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7837 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
7842 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
7844 if ( current_tr->visible &&
7845 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7846 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
7847 marker_begin_move ( t, event->x, event->y );
7853 if ( vtl->tracks_visible )
7854 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7856 if ( params.closest_tp )
7858 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
7859 vtl->current_tpl = params.closest_tpl;
7860 vtl->current_tp_id = params.closest_track_id;
7861 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
7862 trw_layer_tpwin_init ( vtl );
7863 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7864 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7868 if ( vtl->routes_visible )
7869 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
7871 if ( params.closest_tp )
7873 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
7874 vtl->current_tpl = params.closest_tpl;
7875 vtl->current_tp_id = params.closest_track_id;
7876 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
7877 trw_layer_tpwin_init ( vtl );
7878 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7879 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7883 /* these aren't the droids you're looking for */
7887 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7889 tool_ed_t *t = data;
7890 VikViewport *vvp = t->vvp;
7892 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7898 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7901 if ( event->state & GDK_CONTROL_MASK )
7903 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7904 if ( tp && tp != vtl->current_tpl->data )
7905 new_coord = tp->coord;
7907 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7910 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7911 marker_moveto ( t, x, y );
7919 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7921 tool_ed_t *t = data;
7922 VikViewport *vvp = t->vvp;
7924 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7926 if ( event->button != 1)
7931 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7934 if ( event->state & GDK_CONTROL_MASK )
7936 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7937 if ( tp && tp != vtl->current_tpl->data )
7938 new_coord = tp->coord;
7941 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7943 marker_end_move ( t );
7945 /* diff dist is diff from orig */
7947 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7949 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7956 #ifdef VIK_CONFIG_GOOGLE
7957 /*** Route Finder ***/
7958 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
7963 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7966 if ( !vtl ) return FALSE;
7967 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
7968 if ( event->button == 3 && vtl->route_finder_current_track ) {
7970 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
7972 vtl->route_finder_coord = *new_end;
7974 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7975 /* remove last ' to:...' */
7976 if ( vtl->route_finder_current_track->comment ) {
7977 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
7978 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
7979 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
7980 last_to - vtl->route_finder_current_track->comment - 1);
7981 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
7986 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
7987 struct LatLon start, end;
7988 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
7989 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
7992 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
7993 vik_coord_to_latlon ( &(tmp), &end );
7994 vtl->route_finder_coord = tmp; /* for continuations */
7996 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
7997 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
7998 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8000 vtl->route_finder_check_added_track = TRUE;
8001 vtl->route_finder_started = FALSE;
8004 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
8005 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
8006 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
8007 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
8008 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
8009 // NB normally this returns a GPX Route - so subsequent usage of it must lookup via the routes hash
8010 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
8013 /* see if anything was done -- a track was added or appended to */
8014 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8015 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 ) );
8016 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8017 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8018 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8019 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8021 vtl->route_finder_added_track = NULL;
8022 vtl->route_finder_check_added_track = FALSE;
8023 vtl->route_finder_append = FALSE;
8025 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
8027 vtl->route_finder_started = TRUE;
8028 vtl->route_finder_coord = tmp;
8029 vtl->route_finder_current_track = NULL;
8035 /*** Show picture ****/
8037 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8042 /* Params are: vvp, event, last match found or NULL */
8043 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8045 if ( wp->image && wp->visible )
8047 gint x, y, slackx, slacky;
8048 GdkEventButton *event = (GdkEventButton *) params[1];
8050 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8051 slackx = wp->image_width / 2;
8052 slacky = wp->image_height / 2;
8053 if ( x <= event->x + slackx && x >= event->x - slackx
8054 && y <= event->y + slacky && y >= event->y - slacky )
8056 params[2] = wp->image; /* we've found a match. however continue searching
8057 * since we want to find the last match -- that
8058 * is, the match that was drawn last. */
8063 static void trw_layer_show_picture ( gpointer pass_along[6] )
8065 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8067 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8070 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8071 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8072 g_free ( quoted_file );
8073 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8075 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() );
8076 g_error_free ( err );
8079 #endif /* WINDOWS */
8082 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8084 gpointer params[3] = { vvp, event, NULL };
8085 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8087 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8090 static gpointer pass_along[6];
8091 pass_along[0] = vtl;
8092 pass_along[5] = params[2];
8093 trw_layer_show_picture ( pass_along );
8094 return TRUE; /* found a match */
8097 return FALSE; /* go through other layers, searching for a match */
8100 /***************************************************************************
8102 ***************************************************************************/
8105 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8107 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8108 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8111 /* Structure for thumbnail creating data used in the background thread */
8113 VikTrwLayer *vtl; // Layer needed for redrawing
8114 GSList *pics; // Image list
8115 } thumbnail_create_thread_data;
8117 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8119 guint total = g_slist_length(tctd->pics), done = 0;
8120 while ( tctd->pics )
8122 a_thumbnails_create ( (gchar *) tctd->pics->data );
8123 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8125 return -1; /* Abort thread */
8127 tctd->pics = tctd->pics->next;
8130 // Redraw to show the thumbnails as they are now created
8131 if ( IS_VIK_LAYER(tctd->vtl) )
8132 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
8137 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8139 while ( tctd->pics )
8141 g_free ( tctd->pics->data );
8142 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8147 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8149 if ( ! vtl->has_verified_thumbnails )
8151 GSList *pics = NULL;
8152 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8155 gint len = g_slist_length ( pics );
8156 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8157 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8160 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8162 (vik_thr_func) create_thumbnails_thread,
8164 (vik_thr_free_func) thumbnail_create_thread_free,
8172 static const gchar* my_track_colors ( gint ii )
8174 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8186 // Fast and reliable way of returning a colour
8187 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8190 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8192 GHashTableIter iter;
8193 gpointer key, value;
8197 g_hash_table_iter_init ( &iter, vtl->tracks );
8199 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8201 // Tracks get a random spread of colours if not already assigned
8202 if ( ! VIK_TRACK(value)->has_color ) {
8203 gdk_color_parse ( my_track_colors (ii) , &(VIK_TRACK(value)->color) );
8204 VIK_TRACK(value)->has_color = TRUE;
8207 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8210 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8216 g_hash_table_iter_init ( &iter, vtl->routes );
8218 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8220 // Routes get an intermix of reds
8221 if ( ! VIK_TRACK(value)->has_color ) {
8223 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8225 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8226 VIK_TRACK(value)->has_color = TRUE;
8229 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8235 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vp )
8237 trw_layer_verify_thumbnails ( vtl, vp );
8238 trw_layer_track_alloc_colors ( vtl );
8241 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8243 return vtl->coord_mode;
8247 * Uniquify the whole layer
8248 * Also requires the layers panel as the names shown there need updating too
8249 * Returns whether the operation was successful or not
8251 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8254 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8255 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8256 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8262 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8264 vik_coord_convert ( &(wp->coord), *dest_mode );
8267 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
8269 vik_track_convert ( tr, *dest_mode );
8272 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8274 if ( vtl->coord_mode != dest_mode )
8276 vtl->coord_mode = dest_mode;
8277 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8278 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8279 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
8283 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8285 vtl->menu_selection = selection;
8288 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8290 return (vtl->menu_selection);
8293 /* ----------- Downloading maps along tracks --------------- */
8295 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8297 /* TODO: calculating based on current size of viewport */
8298 const gdouble w_at_zoom_0_125 = 0.0013;
8299 const gdouble h_at_zoom_0_125 = 0.0011;
8300 gdouble zoom_factor = zoom_level/0.125;
8302 wh->lat = h_at_zoom_0_125 * zoom_factor;
8303 wh->lon = w_at_zoom_0_125 * zoom_factor;
8305 return 0; /* all OK */
8308 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8310 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8311 (dist->lat >= ABS(to->north_south - from->north_south)))
8314 VikCoord *coord = g_malloc(sizeof(VikCoord));
8315 coord->mode = VIK_COORD_LATLON;
8317 if (ABS(gradient) < 1) {
8318 if (from->east_west > to->east_west)
8319 coord->east_west = from->east_west - dist->lon;
8321 coord->east_west = from->east_west + dist->lon;
8322 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8324 if (from->north_south > to->north_south)
8325 coord->north_south = from->north_south - dist->lat;
8327 coord->north_south = from->north_south + dist->lat;
8328 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8334 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8336 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8337 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8339 VikCoord *next = from;
8341 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8343 list = g_list_prepend(list, next);
8349 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8351 typedef struct _Rect {
8356 #define GLRECT(iter) ((Rect *)((iter)->data))
8359 GList *rects_to_download = NULL;
8362 if (get_download_area_width(vvp, zoom_level, &wh))
8365 GList *iter = tr->trackpoints;
8369 gboolean new_map = TRUE;
8370 VikCoord *cur_coord, tl, br;
8373 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8375 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8376 rect = g_malloc(sizeof(Rect));
8379 rect->center = *cur_coord;
8380 rects_to_download = g_list_prepend(rects_to_download, rect);
8385 gboolean found = FALSE;
8386 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8387 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8398 GList *fillins = NULL;
8399 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8400 /* seems that ATM the function get_next_coord works only for LATLON */
8401 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8402 /* fill-ins for far apart points */
8403 GList *cur_rect, *next_rect;
8404 for (cur_rect = rects_to_download;
8405 (next_rect = cur_rect->next) != NULL;
8406 cur_rect = cur_rect->next) {
8407 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8408 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8409 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8413 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8416 GList *iter = fillins;
8418 cur_coord = (VikCoord *)(iter->data);
8419 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8420 rect = g_malloc(sizeof(Rect));
8423 rect->center = *cur_coord;
8424 rects_to_download = g_list_prepend(rects_to_download, rect);
8429 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8430 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8434 for (iter = fillins; iter; iter = iter->next)
8436 g_list_free(fillins);
8438 if (rects_to_download) {
8439 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8440 g_free(rect_iter->data);
8441 g_list_free(rects_to_download);
8445 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8448 gint selected_map, default_map;
8449 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8450 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8451 gint selected_zoom, default_zoom;
8455 VikTrwLayer *vtl = pass_along[0];
8456 VikLayersPanel *vlp = pass_along[1];
8458 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8459 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8461 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8465 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8467 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8468 int num_maps = g_list_length(vmls);
8471 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8475 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8476 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8478 gchar **np = map_names;
8479 VikMapsLayer **lp = map_layers;
8480 for (i = 0; i < num_maps; i++) {
8481 gboolean dup = FALSE;
8482 vml = (VikMapsLayer *)(vmls->data);
8483 for (j = 0; j < i; j++) { /* no duplicate allowed */
8484 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8491 *np++ = vik_maps_layer_get_map_label(vml);
8497 num_maps = lp - map_layers;
8499 for (default_map = 0; default_map < num_maps; default_map++) {
8500 /* TODO: check for parent layer's visibility */
8501 if (VIK_LAYER(map_layers[default_map])->visible)
8504 default_map = (default_map == num_maps) ? 0 : default_map;
8506 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8507 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8508 if (cur_zoom == zoom_vals[default_zoom])
8511 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8513 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8516 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8519 for (i = 0; i < num_maps; i++)
8520 g_free(map_names[i]);
8528 /**** lowest waypoint number calculation ***/
8529 static gint highest_wp_number_name_to_number(const gchar *name) {
8530 if ( strlen(name) == 3 ) {
8532 if ( n < 100 && name[0] != '0' )
8534 if ( n < 10 && name[0] != '0' )
8542 static void highest_wp_number_reset(VikTrwLayer *vtl)
8544 vtl->highest_wp_number = -1;
8547 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8549 /* if is bigger that top, add it */
8550 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8551 if ( new_wp_num > vtl->highest_wp_number )
8552 vtl->highest_wp_number = new_wp_num;
8555 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8557 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8558 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8559 if ( vtl->highest_wp_number == old_wp_num ) {
8561 vtl->highest_wp_number--;
8563 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8564 /* search down until we find something that *does* exist */
8566 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8567 vtl->highest_wp_number--;
8568 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8573 /* get lowest unused number */
8574 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8577 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8579 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8580 return g_strdup(buf);