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 16
81 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
82 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
83 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
84 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
85 #define VIK_TRW_LAYER_TRACK_GC_SLOW 13
86 #define VIK_TRW_LAYER_TRACK_GC_AVER 14
87 #define VIK_TRW_LAYER_TRACK_GC_FAST 15
89 #define DRAWMODE_BY_TRACK 0
90 #define DRAWMODE_BY_SPEED 1
91 #define DRAWMODE_ALL_BLACK 2
92 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
93 // as we are (re)calculating the colour for every point
98 /* this is how it knows when you click if you are clicking close to a trackpoint. */
99 #define TRACKPOINT_SIZE_APPROX 5
100 #define WAYPOINT_SIZE_APPROX 5
102 #define MIN_STOP_LENGTH 15
103 #define MAX_STOP_LENGTH 86400
104 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
105 /* this is multiplied by user-inputted value from 1-100. */
107 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
109 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
111 FS_XX_SMALL = 0, // 'xx-small'
114 FS_MEDIUM, // DEFAULT
121 struct _VikTrwLayer {
124 GHashTable *tracks_iters;
126 GHashTable *routes_iters;
127 GHashTable *waypoints_iters;
128 GHashTable *waypoints;
129 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
130 gboolean tracks_visible, routes_visible, waypoints_visible;
133 guint8 drawpoints_size;
134 guint8 drawelevation;
135 guint8 elevation_factor;
139 guint8 drawdirections;
140 guint8 drawdirections_size;
141 guint8 line_thickness;
142 guint8 bg_line_thickness;
146 gboolean wp_draw_symbols;
147 font_size_t wp_font_size;
149 gdouble track_draw_speed_factor;
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 {
221 guint16 width, height;
222 gdouble cc; // Cosine factor in track directions
223 gdouble ss; // Sine factor in track directions
224 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 gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
237 static void trw_layer_find_maxmin_tracks ( const gchar *name, 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 gchar *name, VikTrack *track, struct DrawingParams *dp );
244 static void trw_layer_draw_waypoint ( const gchar *name, 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] );
318 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
319 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
320 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
322 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
323 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
324 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
325 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
327 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
328 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
329 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
330 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
331 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
332 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
333 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
334 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
335 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
336 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
337 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
338 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
339 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
340 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
341 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
342 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
343 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
344 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
345 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
346 #ifdef VIK_CONFIG_GOOGLE
347 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
348 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
351 static void cached_pixbuf_free ( CachedPixbuf *cp );
352 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
354 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
355 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
357 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
358 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
360 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
361 static void highest_wp_number_reset(VikTrwLayer *vtl);
362 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
363 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
365 // Note for the following tool GtkRadioActionEntry texts:
366 // the very first text value is an internal name not displayed anywhere
367 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
368 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
369 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
370 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
371 static VikToolInterface trw_layer_tools[] = {
372 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
373 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
374 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
376 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
378 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
379 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
380 (VikToolMouseFunc) tool_new_track_click,
381 (VikToolMouseMoveFunc) tool_new_track_move,
382 (VikToolMouseFunc) tool_new_track_release,
383 (VikToolKeyFunc) tool_new_track_key_press,
384 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
385 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
387 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
388 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
389 (VikToolMouseFunc) tool_new_route_click,
390 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
391 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
392 (VikToolKeyFunc) tool_new_track_key_press, // -/#
393 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
394 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
396 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
397 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
398 (VikToolMouseFunc) tool_edit_waypoint_click,
399 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
400 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
402 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
404 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
405 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
406 (VikToolMouseFunc) tool_edit_trackpoint_click,
407 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
408 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
410 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
412 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
413 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
414 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
416 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
418 #ifdef VIK_CONFIG_GOOGLE
419 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
420 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
421 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
423 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
426 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_CREATE_ROUTE, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
428 /****** PARAMETERS ******/
430 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
431 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
433 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
434 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
436 #define MIN_POINT_SIZE 2
437 #define MAX_POINT_SIZE 10
439 #define MIN_ARROW_SIZE 3
440 #define MAX_ARROW_SIZE 20
442 static VikLayerParamScale params_scales[] = {
443 /* min max step digits */
444 { 1, 10, 1, 0 }, /* line_thickness */
445 { 0, 100, 1, 0 }, /* track draw speed factor */
446 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
447 /* 5 * step == how much to turn */
448 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
449 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
450 { 5, 500, 5, 0 }, // 5: image cache_size - " "
451 { 0, 8, 1, 0 }, // 6: Background line thickness
452 { 1, 64, 1, 0 }, /* wpsize */
453 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
454 { 1, 100, 1, 0 }, // 9: elevation factor
455 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
456 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
459 static gchar* params_font_sizes[] = {
460 N_("Extra Extra Small"),
466 N_("Extra Extra Large"),
469 VikLayerParam trw_layer_params[] = {
470 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
471 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
472 { "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
474 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
475 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
476 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
477 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
478 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
479 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
480 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
481 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
482 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
484 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
485 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
487 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
488 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
489 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
491 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
492 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
493 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
494 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
495 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
496 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
497 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
498 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
499 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
501 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
502 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
503 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
504 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
507 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
509 // Sublayer visibilities
547 *** 1) Add to trw_layer_params and enumeration
548 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
551 /****** END PARAMETERS ******/
553 static VikTrwLayer* trw_layer_new ( gint drawmode );
554 /* Layer Interface function definitions */
555 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
556 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
557 static void trw_layer_free ( VikTrwLayer *trwlayer );
558 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
559 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
560 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
561 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
562 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
563 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
564 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
565 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
566 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
567 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
568 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
569 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
570 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
571 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
572 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
573 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
574 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
575 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
576 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
577 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
578 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
579 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
580 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
581 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
582 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
583 /* End Layer Interface function definitions */
585 VikLayerInterface vik_trw_layer_interface = {
592 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
596 params_groups, /* params_groups */
597 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
601 (VikLayerFuncCreate) trw_layer_create,
602 (VikLayerFuncRealize) trw_layer_realize,
603 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
604 (VikLayerFuncFree) trw_layer_free,
606 (VikLayerFuncProperties) NULL,
607 (VikLayerFuncDraw) trw_layer_draw,
608 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
610 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
611 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
613 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
614 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
616 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
617 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
618 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
619 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
620 (VikLayerFuncLayerSelected) trw_layer_selected,
622 (VikLayerFuncMarshall) trw_layer_marshall,
623 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
625 (VikLayerFuncSetParam) trw_layer_set_param,
626 (VikLayerFuncGetParam) trw_layer_get_param,
628 (VikLayerFuncReadFileData) a_gpspoint_read_file,
629 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
631 (VikLayerFuncDeleteItem) trw_layer_del_item,
632 (VikLayerFuncCutItem) trw_layer_cut_item,
633 (VikLayerFuncCopyItem) trw_layer_copy_item,
634 (VikLayerFuncPasteItem) trw_layer_paste_item,
635 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
637 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
639 (VikLayerFuncSelectClick) trw_layer_select_click,
640 (VikLayerFuncSelectMove) trw_layer_select_move,
641 (VikLayerFuncSelectRelease) trw_layer_select_release,
642 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
651 GType vik_trw_layer_get_type ()
653 static GType vtl_type = 0;
657 static const GTypeInfo vtl_info =
659 sizeof (VikTrwLayerClass),
660 NULL, /* base_init */
661 NULL, /* base_finalize */
662 NULL, /* class init */
663 NULL, /* class_finalize */
664 NULL, /* class_data */
665 sizeof (VikTrwLayer),
667 NULL /* instance init */
669 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
675 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
677 static gpointer pass_along[6];
683 pass_along[1] = NULL;
684 pass_along[2] = GINT_TO_POINTER (subtype);
685 pass_along[3] = sublayer;
686 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
687 pass_along[5] = NULL;
689 trw_layer_delete_item ( pass_along );
692 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
694 static gpointer pass_along[6];
700 pass_along[1] = NULL;
701 pass_along[2] = GINT_TO_POINTER (subtype);
702 pass_along[3] = sublayer;
703 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
704 pass_along[5] = NULL;
706 trw_layer_copy_item_cb(pass_along);
707 trw_layer_cut_item_cb(pass_along);
710 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
712 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
713 gint subtype = GPOINTER_TO_INT (pass_along[2]);
714 gpointer * sublayer = pass_along[3];
718 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
722 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
723 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
724 if ( wp && wp->name )
727 name = NULL; // Broken :(
729 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
730 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
731 if ( trk && trk->name )
734 name = NULL; // Broken :(
737 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
738 if ( trk && trk->name )
741 name = NULL; // Broken :(
744 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
745 subtype, len, name, data);
749 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
751 trw_layer_copy_item_cb(pass_along);
752 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
753 trw_layer_delete_item(pass_along);
756 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
758 // Slightly cheating method, routing via the panels capability
759 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
762 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
773 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
775 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
776 // 'Simple' memory copy of byte array from the marshalling above
777 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
778 fi = g_malloc ( *len );
780 memcpy(fi->data, id, il);
781 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
782 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
783 // less magic than before...
784 *len = sizeof(FlatItem) + 1 + il;
785 fi = g_malloc ( *len );
787 memcpy(fi->data, id, il);
789 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
790 // less magic than before...
791 *len = sizeof(FlatItem) + 1 + il;
792 fi = g_malloc ( *len );
794 memcpy(fi->data, id, il);
798 *item = (guint8 *)fi;
801 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
803 FlatItem *fi = (FlatItem *) item;
805 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
810 w = vik_waypoint_unmarshall(fi->data, fi->len);
811 // When copying - we'll create a new name based on the original
812 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
813 vik_trw_layer_add_waypoint ( vtl, name, w );
814 waypoint_convert (NULL, w, &vtl->coord_mode);
816 // Consider if redraw necessary for the new item
817 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
818 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
821 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
826 t = vik_track_unmarshall(fi->data, fi->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 && fi )
842 t = vik_track_unmarshall(fi->data, fi->len);
843 // When copying - we'll create a new name based on the original
844 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
845 vik_trw_layer_add_route ( vtl, name, t );
846 vik_track_convert (t, vtl->coord_mode);
848 // Consider if redraw necessary for the new item
849 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
850 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
856 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
863 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
867 case PARAM_TV: vtl->tracks_visible = data.b; break;
868 case PARAM_WV: vtl->waypoints_visible = data.b; break;
869 case PARAM_RV: vtl->routes_visible = data.b; break;
870 case PARAM_DM: vtl->drawmode = data.u; break;
871 case PARAM_DP: vtl->drawpoints = data.b; break;
873 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
874 vtl->drawpoints_size = data.u;
876 case PARAM_DE: vtl->drawelevation = data.b; break;
877 case PARAM_DS: vtl->drawstops = data.b; break;
878 case PARAM_DL: vtl->drawlines = data.b; break;
879 case PARAM_DD: vtl->drawdirections = data.b; break;
881 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
882 vtl->drawdirections_size = data.u;
884 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
885 vtl->stop_length = data.u;
887 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
888 vtl->elevation_factor = data.u;
890 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
892 vtl->line_thickness = data.u;
893 trw_layer_new_track_gcs ( vtl, vp );
896 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
898 vtl->bg_line_thickness = data.u;
899 trw_layer_new_track_gcs ( vtl, vp );
902 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
903 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
904 case PARAM_DLA: vtl->drawlabels = data.b; break;
905 case PARAM_DI: vtl->drawimages = data.b; break;
906 case PARAM_IS: if ( data.u != vtl->image_size )
908 vtl->image_size = data.u;
909 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
910 g_queue_free ( vtl->image_cache );
911 vtl->image_cache = g_queue_new ();
914 case PARAM_IA: vtl->image_alpha = data.u; break;
915 case PARAM_ICS: vtl->image_cache_size = data.u;
916 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
917 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
919 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
920 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
921 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
922 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
923 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
924 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
925 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
926 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
931 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
933 VikLayerParamData rv;
936 case PARAM_TV: rv.b = vtl->tracks_visible; break;
937 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
938 case PARAM_RV: rv.b = vtl->routes_visible; break;
939 case PARAM_DM: rv.u = vtl->drawmode; break;
940 case PARAM_DP: rv.b = vtl->drawpoints; break;
941 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
942 case PARAM_DE: rv.b = vtl->drawelevation; break;
943 case PARAM_EF: rv.u = vtl->elevation_factor; break;
944 case PARAM_DS: rv.b = vtl->drawstops; break;
945 case PARAM_SL: rv.u = vtl->stop_length; break;
946 case PARAM_DL: rv.b = vtl->drawlines; break;
947 case PARAM_DD: rv.b = vtl->drawdirections; break;
948 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
949 case PARAM_LT: rv.u = vtl->line_thickness; break;
950 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
951 case PARAM_DLA: rv.b = vtl->drawlabels; break;
952 case PARAM_DI: rv.b = vtl->drawimages; break;
953 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
954 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
955 case PARAM_IS: rv.u = vtl->image_size; break;
956 case PARAM_IA: rv.u = vtl->image_alpha; break;
957 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
958 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
959 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
960 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
961 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
962 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
963 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
964 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
965 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
970 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
981 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
982 a_gpx_write_file(vtl, f, NULL);
983 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
986 g_file_get_contents(tmpname, &dd, &dl, NULL);
987 *len = sizeof(pl) + pl + dl;
988 *data = g_malloc(*len);
989 memcpy(*data, &pl, sizeof(pl));
990 memcpy(*data + sizeof(pl), pd, pl);
991 memcpy(*data + sizeof(pl) + pl, dd, dl);
1000 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1002 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1008 memcpy(&pl, data, sizeof(pl));
1010 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
1013 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
1014 g_critical("couldn't open temp file");
1017 fwrite(data, len - pl - sizeof(pl), 1, f);
1019 a_gpx_read_file(rv, f);
1027 // Keep interesting hash function at least visible
1029 static guint strcase_hash(gconstpointer v)
1031 // 31 bit hash function
1034 gchar s[128]; // malloc is too slow for reading big files
1037 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1038 p[i] = toupper(t[i]);
1044 for (p += 1; *p != '\0'; p++)
1045 h = (h << 5) - h + *p;
1052 static VikTrwLayer* trw_layer_new ( gint drawmode )
1054 if (trw_layer_params[PARAM_DM].widget_data == NULL)
1055 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
1056 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
1057 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
1059 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1060 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1062 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1063 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1065 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1066 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1067 // and with normal PC processing capabilities - it has negligibile performance impact
1068 // This also minimized the amount of rework - as the management of the hash tables already exists.
1070 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1071 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1072 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1074 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1075 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1076 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1077 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1078 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1079 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1081 /* TODO: constants at top */
1082 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1083 rv->drawmode = drawmode;
1084 rv->drawpoints = TRUE;
1085 rv->drawpoints_size = MIN_POINT_SIZE;
1086 rv->drawdirections_size = 5;
1087 rv->drawstops = FALSE;
1088 rv->drawelevation = FALSE;
1089 rv->elevation_factor = 30;
1090 rv->stop_length = 60;
1091 rv->drawlines = TRUE;
1092 rv->wplabellayout = NULL;
1093 rv->wp_right_click_menu = NULL;
1094 rv->track_right_click_menu = NULL;
1095 rv->waypoint_gc = NULL;
1096 rv->waypoint_text_gc = NULL;
1097 rv->waypoint_bg_gc = NULL;
1098 rv->track_gc = NULL;
1099 rv->track_draw_speed_factor = 30.0;
1100 rv->line_thickness = 1;
1101 rv->bg_line_thickness = 0;
1102 rv->current_wp = NULL;
1103 rv->current_wp_id = NULL;
1104 rv->current_track = NULL;
1105 rv->current_tpl = NULL;
1106 rv->current_tp_track = NULL;
1107 rv->current_tp_id = NULL;
1108 rv->moving_tp = FALSE;
1109 rv->moving_wp = FALSE;
1111 rv->draw_sync_done = TRUE;
1112 rv->draw_sync_do = TRUE;
1114 rv->route_finder_started = FALSE;
1115 rv->route_finder_check_added_track = FALSE;
1116 rv->route_finder_current_track = NULL;
1117 rv->route_finder_append = FALSE;
1119 rv->waypoint_rightclick = FALSE;
1121 rv->image_cache = g_queue_new();
1122 rv->image_size = 64;
1123 rv->image_alpha = 255;
1124 rv->image_cache_size = 300;
1125 rv->drawimages = TRUE;
1126 rv->drawlabels = TRUE;
1131 static void trw_layer_free ( VikTrwLayer *trwlayer )
1133 g_hash_table_destroy(trwlayer->waypoints);
1134 g_hash_table_destroy(trwlayer->tracks);
1136 /* ODC: replace with GArray */
1137 trw_layer_free_track_gcs ( trwlayer );
1139 if ( trwlayer->wp_right_click_menu )
1140 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1142 if ( trwlayer->track_right_click_menu )
1143 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1145 if ( trwlayer->wplabellayout != NULL)
1146 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1148 if ( trwlayer->waypoint_gc != NULL )
1149 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1151 if ( trwlayer->waypoint_text_gc != NULL )
1152 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1154 if ( trwlayer->waypoint_bg_gc != NULL )
1155 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1157 if ( trwlayer->tpwin != NULL )
1158 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1160 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1161 g_queue_free ( trwlayer->image_cache );
1164 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1168 dp->xmpp = vik_viewport_get_xmpp ( vp );
1169 dp->ympp = vik_viewport_get_ympp ( vp );
1170 dp->width = vik_viewport_get_width ( vp );
1171 dp->height = vik_viewport_get_height ( vp );
1172 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1173 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1175 dp->center = vik_viewport_get_center ( vp );
1176 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1177 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1182 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1183 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1184 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1186 dp->ce1 = dp->center->east_west-w2;
1187 dp->ce2 = dp->center->east_west+w2;
1188 dp->cn1 = dp->center->north_south-h2;
1189 dp->cn2 = dp->center->north_south+h2;
1190 } else if ( dp->lat_lon ) {
1191 VikCoord upperleft, bottomright;
1192 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1193 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1194 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1195 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1196 dp->ce1 = upperleft.east_west;
1197 dp->ce2 = bottomright.east_west;
1198 dp->cn1 = bottomright.north_south;
1199 dp->cn2 = upperleft.north_south;
1202 dp->track_gc_iter = 0;
1206 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1207 * Here a simple traffic like light colour system is used:
1208 * . slow points are red
1209 * . average is yellow
1210 * . fast points are green
1212 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1215 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1216 if ( average_speed > 0 ) {
1217 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1218 if ( rv < low_speed )
1219 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1220 else if ( rv > high_speed )
1221 return VIK_TRW_LAYER_TRACK_GC_FAST;
1223 return VIK_TRW_LAYER_TRACK_GC_AVER;
1226 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1229 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1231 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1232 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1233 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1234 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1237 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1239 /* TODO: this function is a mess, get rid of any redundancy */
1240 GList *list = track->trackpoints;
1242 gboolean useoldvals = TRUE;
1244 gboolean drawpoints;
1246 gboolean drawelevation;
1247 gdouble min_alt, max_alt, alt_diff = 0;
1249 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1250 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1253 if ( dp->vtl->drawelevation )
1255 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1256 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1257 alt_diff = max_alt - min_alt;
1260 if ( ! track->visible )
1263 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1264 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1265 trw_layer_draw_track ( name, track, dp, TRUE );
1267 if ( draw_track_outline )
1268 drawpoints = drawstops = FALSE;
1270 drawpoints = dp->vtl->drawpoints;
1271 drawstops = dp->vtl->drawstops;
1274 gboolean drawing_highlight = FALSE;
1275 /* Current track - used for creation */
1276 if ( track == dp->vtl->current_track )
1277 main_gc = dp->vtl->current_track_gc;
1279 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1280 /* Draw all tracks of the layer in special colour */
1281 /* if track is member of selected layer or is the current selected track
1282 then draw in the highlight colour.
1283 NB this supercedes the drawmode */
1284 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1285 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) ||
1286 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) ||
1287 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1288 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1289 drawing_highlight = TRUE;
1292 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1293 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1295 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1299 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1300 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1302 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1307 int x, y, oldx, oldy;
1308 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1310 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1312 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1314 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1316 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1317 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1323 gdouble average_speed = 0.0;
1324 gdouble low_speed = 0.0;
1325 gdouble high_speed = 0.0;
1326 // If necessary calculate these values - which is done only once per track redraw
1327 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1328 // the percentage factor away from the average speed determines transistions between the levels
1329 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1330 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1331 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1334 while ((list = g_list_next(list)))
1336 tp = VIK_TRACKPOINT(list->data);
1337 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1339 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1340 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1341 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1342 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1343 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1345 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1348 * If points are the same in display coordinates, don't draw.
1350 if ( useoldvals && x == oldx && y == oldy )
1352 // Still need to process points to ensure 'stops' are drawn if required
1353 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1354 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1355 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 );
1360 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1361 if ( drawpoints || dp->vtl->drawlines ) {
1362 // setup main_gc for both point and line drawing
1363 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1364 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1365 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1369 if ( drawpoints && ! draw_track_outline )
1374 * The concept of drawing stops is that a trackpoint
1375 * that is if the next trackpoint has a timestamp far into
1376 * the future, we draw a circle of 6x trackpoint size,
1377 * instead of a rectangle of 2x trackpoint size.
1378 * This is drawn first so the trackpoint will be drawn on top
1381 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1382 /* Stop point. Draw 6x circle. */
1383 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 );
1385 /* Regular point - draw 2x square. */
1386 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1389 /* Final point - draw 4x circle. */
1390 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 );
1393 if ((!tp->newsegment) && (dp->vtl->drawlines))
1396 /* UTM only: zone check */
1397 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1398 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1401 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1403 if ( draw_track_outline ) {
1404 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1408 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1410 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1412 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1417 tmp[1].y = oldy-FIXALTITUDE(list->data);
1419 tmp[2].y = y-FIXALTITUDE(list->next->data);
1424 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1425 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1427 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1428 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1430 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1435 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1436 // Draw an arrow at the mid point to show the direction of the track
1437 // Code is a rework from vikwindow::draw_ruler()
1438 gint midx = (oldx + x) / 2;
1439 gint midy = (oldy + y) / 2;
1441 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1442 // Avoid divide by zero and ensure at least 1 pixel big
1444 gdouble dx = (oldx - midx) / len;
1445 gdouble dy = (oldy - midy) / len;
1446 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1447 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1457 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1459 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1460 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1462 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1464 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1465 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1466 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1470 * If points are the same in display coordinates, don't draw.
1472 if ( x != oldx || y != oldy )
1474 if ( draw_track_outline )
1475 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1477 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1483 * If points are the same in display coordinates, don't draw.
1485 if ( x != oldx && y != oldy )
1487 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1488 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1496 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1497 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
1498 dp->track_gc_iter = 0;
1501 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1502 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1504 trw_layer_draw_track ( name, track, dp, FALSE );
1507 static void cached_pixbuf_free ( CachedPixbuf *cp )
1509 g_object_unref ( G_OBJECT(cp->pixbuf) );
1510 g_free ( cp->image );
1513 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1515 return strcmp ( cp->image, name );
1518 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1521 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1522 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1523 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1526 GdkPixbuf *sym = NULL;
1527 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1529 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1531 if ( wp->image && dp->vtl->drawimages )
1533 GdkPixbuf *pixbuf = NULL;
1536 if ( dp->vtl->image_alpha == 0)
1539 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1541 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1544 gchar *image = wp->image;
1545 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1546 if ( ! regularthumb )
1548 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1549 image = "\x12\x00"; /* this shouldn't occur naturally. */
1553 CachedPixbuf *cp = NULL;
1554 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1555 if ( dp->vtl->image_size == 128 )
1556 cp->pixbuf = regularthumb;
1559 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1560 g_assert ( cp->pixbuf );
1561 g_object_unref ( G_OBJECT(regularthumb) );
1563 cp->image = g_strdup ( image );
1565 /* needed so 'click picture' tool knows how big the pic is; we don't
1566 * store it in cp because they may have been freed already. */
1567 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1568 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1570 g_queue_push_head ( dp->vtl->image_cache, cp );
1571 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1572 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1574 pixbuf = cp->pixbuf;
1578 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1584 w = gdk_pixbuf_get_width ( pixbuf );
1585 h = gdk_pixbuf_get_height ( pixbuf );
1587 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1589 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1590 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1591 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1592 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1593 // Highlighted - so draw a little border around the chosen one
1594 // single line seems a little weak so draw 2 of them
1595 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1596 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1597 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1598 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1601 if ( dp->vtl->image_alpha == 255 )
1602 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1604 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1606 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1610 /* DRAW ACTUAL DOT */
1611 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1612 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 );
1614 else if ( wp == dp->vtl->current_wp ) {
1615 switch ( dp->vtl->wp_symbol ) {
1616 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;
1617 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;
1618 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;
1619 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 );
1620 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 );
1624 switch ( dp->vtl->wp_symbol ) {
1625 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;
1626 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;
1627 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;
1628 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 );
1629 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;
1633 if ( dp->vtl->drawlabels )
1635 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1636 gint label_x, label_y;
1638 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1640 // Could this stored in the waypoint rather than recreating each pass?
1641 gchar *fsize = NULL;
1642 switch (dp->vtl->wp_font_size) {
1643 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1644 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1645 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1646 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1647 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1648 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1649 default: fsize = g_strdup ( "medium" ); break;
1652 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1654 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1655 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1657 // Fallback if parse failure
1658 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1660 g_free ( wp_label_markup );
1663 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1664 label_x = x - width/2;
1666 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1668 label_y = y - dp->vtl->wp_size - height - 2;
1670 /* if highlight mode on, then draw background text in highlight colour */
1671 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1672 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1673 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1674 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1675 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1677 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1680 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1682 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1687 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1689 static struct DrawingParams dp;
1690 g_assert ( l != NULL );
1692 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1694 if ( l->tracks_visible )
1695 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1697 if ( l->routes_visible )
1698 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1700 if (l->waypoints_visible)
1701 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1704 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1707 if ( vtl->track_bg_gc )
1709 g_object_unref ( vtl->track_bg_gc );
1710 vtl->track_bg_gc = NULL;
1712 if ( vtl->current_track_gc )
1714 g_object_unref ( vtl->current_track_gc );
1715 vtl->current_track_gc = NULL;
1717 if ( vtl->current_track_newpoint_gc )
1719 g_object_unref ( vtl->current_track_newpoint_gc );
1720 vtl->current_track_newpoint_gc = NULL;
1723 if ( ! vtl->track_gc )
1725 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1726 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1727 g_array_free ( vtl->track_gc, TRUE );
1728 vtl->track_gc = NULL;
1731 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1733 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1734 gint width = vtl->line_thickness;
1736 if ( vtl->track_gc )
1737 trw_layer_free_track_gcs ( vtl );
1739 if ( vtl->track_bg_gc )
1740 g_object_unref ( vtl->track_bg_gc );
1741 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1743 // Ensure new track drawing heeds line thickness setting
1744 // however always have a minium of 2, as 1 pixel is really narrow
1745 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1747 if ( vtl->current_track_gc )
1748 g_object_unref ( vtl->current_track_gc );
1749 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1750 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1752 // 'newpoint' gc is exactly the same as the current track gc
1753 if ( vtl->current_track_newpoint_gc )
1754 g_object_unref ( vtl->current_track_newpoint_gc );
1755 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1756 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1758 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1760 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1762 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1763 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1764 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1765 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1766 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1767 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1768 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1769 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1770 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1771 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1773 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1775 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1777 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1778 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1779 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1781 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1784 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1786 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1787 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1789 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1790 /* early exit, as the rest is GUI related */
1794 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1795 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1797 trw_layer_new_track_gcs ( rv, vp );
1799 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1800 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1801 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1802 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1804 rv->has_verified_thumbnails = FALSE;
1805 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1807 rv->wp_draw_symbols = TRUE;
1808 rv->wp_font_size = FS_MEDIUM;
1810 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1812 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1817 #define SMALL_ICON_SIZE 18
1819 * Can accept a null symbol, and may return null value
1821 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1823 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1824 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1825 // So needing a small icon for the treeview may need some resizing:
1826 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1827 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1831 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1833 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1835 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1836 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1838 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1841 *new_iter = *((GtkTreeIter *) pass_along[1]);
1842 if ( track->is_route )
1843 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1845 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1847 if ( ! track->visible )
1848 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1851 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1853 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1855 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1856 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 );
1858 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 );
1861 *new_iter = *((GtkTreeIter *) pass_along[1]);
1862 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1864 if ( ! wp->visible )
1865 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1868 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1870 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1871 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1873 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1877 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1879 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1880 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1882 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1886 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1888 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1889 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1891 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1895 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1898 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1900 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1901 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1902 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1904 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1907 if ( g_hash_table_size (vtl->routes) > 0 ) {
1909 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
1911 pass_along[0] = &(vtl->routes_iter);
1912 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
1914 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
1916 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
1919 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
1920 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
1922 pass_along[0] = &(vtl->waypoints_iter);
1923 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1925 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1927 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1932 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1936 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1937 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1938 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
1939 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1941 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1943 return (t->visible ^= 1);
1947 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1949 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1951 return (t->visible ^= 1);
1955 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
1957 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
1959 return (t->visible ^= 1);
1968 * Return a property about tracks for this layer
1970 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1972 return vtl->line_thickness;
1975 // Structure to hold multiple track information for a layer
1984 * Build up layer multiple track information via updating the tooltip_tracks structure
1986 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1988 tt->length = tt->length + vik_track_get_length (tr);
1990 // Ensure times are available
1991 if ( tr->trackpoints &&
1992 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1993 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1996 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1997 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1999 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2000 // Hence initialize to the first 'proper' value
2001 if ( tt->start_time == 0 )
2002 tt->start_time = t1;
2003 if ( tt->end_time == 0 )
2006 // Update find the earliest / last times
2007 if ( t1 < tt->start_time )
2008 tt->start_time = t1;
2009 if ( t2 > tt->end_time )
2012 // Keep track of total time
2013 // there maybe gaps within a track (eg segments)
2014 // but this should be generally good enough for a simple indicator
2015 tt->duration = tt->duration + (int)(t2-t1);
2020 * Generate tooltip text for the layer.
2021 * This is relatively complicated as it considers information for
2022 * no tracks, a single track or multiple tracks
2023 * (which may or may not have timing information)
2025 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2036 static gchar tmp_buf[128];
2039 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2041 // Safety check - I think these should always be valid
2042 if ( vtl->tracks && vtl->waypoints ) {
2043 tooltip_tracks tt = { 0.0, 0, 0 };
2044 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2046 GDate* gdate_start = g_date_new ();
2047 g_date_set_time_t (gdate_start, tt.start_time);
2049 GDate* gdate_end = g_date_new ();
2050 g_date_set_time_t (gdate_end, tt.end_time);
2052 if ( g_date_compare (gdate_start, gdate_end) ) {
2053 // Dates differ so print range on separate line
2054 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2055 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2056 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2059 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2060 if ( tt.start_time != 0 )
2061 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2065 if ( tt.length > 0.0 ) {
2066 gdouble len_in_units;
2068 // Setup info dependent on distance units
2069 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2070 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2071 len_in_units = VIK_METERS_TO_MILES(tt.length);
2074 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2075 len_in_units = tt.length/1000.0;
2078 // Timing information if available
2080 if ( tt.duration > 0 ) {
2081 g_snprintf (tbuf1, sizeof(tbuf1),
2082 _(" in %d:%02d hrs:mins"),
2083 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2085 g_snprintf (tbuf2, sizeof(tbuf2),
2086 _("\n%sTotal Length %.1f %s%s"),
2087 tbuf3, len_in_units, tbuf4, tbuf1);
2090 // Put together all the elements to form compact tooltip text
2091 g_snprintf (tmp_buf, sizeof(tmp_buf),
2092 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2093 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2095 g_date_free (gdate_start);
2096 g_date_free (gdate_end);
2103 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2107 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2109 // Very simple tooltip - may expand detail in the future...
2110 static gchar tmp_buf[32];
2111 g_snprintf (tmp_buf, sizeof(tmp_buf),
2113 g_hash_table_size (l->tracks));
2117 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2119 // Very simple tooltip - may expand detail in the future...
2120 static gchar tmp_buf[32];
2121 g_snprintf (tmp_buf, sizeof(tmp_buf),
2123 g_hash_table_size (l->routes));
2128 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2129 // Same tooltip for a route
2130 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2133 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2134 tr = g_hash_table_lookup ( l->tracks, sublayer );
2136 tr = g_hash_table_lookup ( l->routes, sublayer );
2139 // Could be a better way of handling strings - but this works...
2140 gchar time_buf1[20];
2141 gchar time_buf2[20];
2142 time_buf1[0] = '\0';
2143 time_buf2[0] = '\0';
2144 static gchar tmp_buf[100];
2145 // Compact info: Short date eg (11/20/99), duration and length
2146 // Hopefully these are the things that are most useful and so promoted into the tooltip
2147 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2148 // %x The preferred date representation for the current locale without the time.
2149 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2150 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2151 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2153 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2156 // Get length and consider the appropriate distance units
2157 gdouble tr_len = vik_track_get_length(tr);
2158 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2159 switch (dist_units) {
2160 case VIK_UNITS_DISTANCE_KILOMETRES:
2161 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2163 case VIK_UNITS_DISTANCE_MILES:
2164 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2173 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2175 // Very simple tooltip - may expand detail in the future...
2176 static gchar tmp_buf[32];
2177 g_snprintf (tmp_buf, sizeof(tmp_buf),
2179 g_hash_table_size (l->waypoints));
2183 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2185 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2186 // NB It's OK to return NULL
2197 * Function to show basic track point information on the statusbar
2199 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2202 switch (a_vik_get_units_height ()) {
2203 case VIK_UNITS_HEIGHT_FEET:
2204 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2207 //VIK_UNITS_HEIGHT_METRES:
2208 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2213 if ( trkpt->has_timestamp ) {
2214 // Compact date time format
2215 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2219 // Position is put later on, as this bit may not be seen if the display is not big enough,
2220 // one can easily use the current pointer position to see this if needed
2221 gchar *lat = NULL, *lon = NULL;
2222 static struct LatLon ll;
2223 vik_coord_to_latlon (&(trkpt->coord), &ll);
2224 a_coords_latlon_to_string ( &ll, &lat, &lon );
2227 // Again is put later on, as this bit may not be seen if the display is not big enough
2228 // trackname can be seen from the treeview (when enabled)
2229 // Also name could be very long to not leave room for anything else
2232 if ( vtl->current_tp_track ) {
2233 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2236 // Combine parts to make overall message
2237 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2238 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2245 * Function to show basic waypoint information on the statusbar
2247 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2250 switch (a_vik_get_units_height ()) {
2251 case VIK_UNITS_HEIGHT_FEET:
2252 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2255 //VIK_UNITS_HEIGHT_METRES:
2256 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2260 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2261 // one can easily use the current pointer position to see this if needed
2262 gchar *lat = NULL, *lon = NULL;
2263 static struct LatLon ll;
2264 vik_coord_to_latlon (&(wpt->coord), &ll);
2265 a_coords_latlon_to_string ( &ll, &lat, &lon );
2267 // Combine parts to make overall message
2270 // Add comment if available
2271 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2273 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2274 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2281 * General layer selection function, find out which bit is selected and take appropriate action
2283 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2286 l->current_wp = NULL;
2287 l->current_wp_id = NULL;
2288 trw_layer_cancel_current_tp ( l, FALSE );
2291 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2295 case VIK_TREEVIEW_TYPE_LAYER:
2297 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2298 /* Mark for redraw */
2303 case VIK_TREEVIEW_TYPE_SUBLAYER:
2307 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2309 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2310 /* Mark for redraw */
2314 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2316 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2317 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2318 /* Mark for redraw */
2322 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2324 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2325 /* Mark for redraw */
2329 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2331 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2332 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2333 /* Mark for redraw */
2337 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2339 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2340 /* Mark for redraw */
2344 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2346 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2348 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2349 // Show some waypoint info
2350 set_statusbar_msg_info_wpt ( l, wpt );
2351 /* Mark for redraw */
2358 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2367 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2372 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2377 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2382 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2384 return l->waypoints;
2388 * ATM use a case sensitive find
2389 * Finds the first one
2391 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2393 if ( wp && wp->name )
2394 if ( ! strcmp ( wp->name, name ) )
2400 * Get waypoint by name - not guaranteed to be unique
2401 * Finds the first one
2403 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2405 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2409 * ATM use a case sensitive find
2410 * Finds the first one
2412 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2414 if ( trk && trk->name )
2415 if ( ! strcmp ( trk->name, name ) )
2421 * Get track by name - not guaranteed to be unique
2422 * Finds the first one
2424 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2426 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2430 * Get route by name - not guaranteed to be unique
2431 * Finds the first one
2433 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2435 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2438 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2440 static VikCoord fixme;
2441 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2442 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2443 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2444 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2445 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2446 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2447 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2448 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2449 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2452 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2454 GList *tr = trk->trackpoints;
2455 static VikCoord fixme;
2459 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2460 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2461 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2462 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2463 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2464 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2465 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2466 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2467 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2472 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2474 // Continually reuse maxmin to find the latest maximum and minimum values
2475 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2476 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2477 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2480 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2482 /* 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... */
2483 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2484 trw_layer_find_maxmin (vtl, maxmin);
2485 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2489 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2490 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2495 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2498 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2499 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2501 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2504 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2506 /* First set the center [in case previously viewing from elsewhere] */
2507 /* Then loop through zoom levels until provided positions are in view */
2508 /* This method is not particularly fast - but should work well enough */
2509 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2511 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2512 vik_viewport_set_center_coord ( vvp, &coord );
2514 /* Convert into definite 'smallest' and 'largest' positions */
2515 struct LatLon minmin;
2516 if ( maxmin[0].lat < maxmin[1].lat )
2517 minmin.lat = maxmin[0].lat;
2519 minmin.lat = maxmin[1].lat;
2521 struct LatLon maxmax;
2522 if ( maxmin[0].lon > maxmin[1].lon )
2523 maxmax.lon = maxmin[0].lon;
2525 maxmax.lon = maxmin[1].lon;
2527 /* Never zoom in too far - generally not that useful, as too close ! */
2528 /* Always recalculate the 'best' zoom level */
2530 vik_viewport_set_zoom ( vvp, zoom );
2532 gdouble min_lat, max_lat, min_lon, max_lon;
2533 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2534 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2535 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2536 /* NB I think the logic used in this test to determine if the bounds is within view
2537 fails if track goes across 180 degrees longitude.
2538 Hopefully that situation is not too common...
2539 Mind you viking doesn't really do edge locations to well anyway */
2540 if ( min_lat < minmin.lat &&
2541 max_lat > minmin.lat &&
2542 min_lon < maxmax.lon &&
2543 max_lon > maxmax.lon )
2544 /* Found within zoom level */
2549 vik_viewport_set_zoom ( vvp, zoom );
2553 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2555 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2556 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2557 trw_layer_find_maxmin (vtl, maxmin);
2558 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2561 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2566 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2568 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])) ) ) {
2569 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2572 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2575 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2577 GtkWidget *file_selector;
2579 gboolean failed = FALSE;
2580 file_selector = gtk_file_chooser_dialog_new (title,
2582 GTK_FILE_CHOOSER_ACTION_SAVE,
2583 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2584 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2586 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2588 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2590 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2591 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2593 gtk_widget_hide ( file_selector );
2594 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2599 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2601 gtk_widget_hide ( file_selector );
2602 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2607 gtk_widget_destroy ( file_selector );
2609 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2612 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2614 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2617 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2619 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2622 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2624 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2625 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2626 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2627 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2629 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2631 g_free ( auto_save_name );
2634 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2636 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2637 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2638 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2639 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2641 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2643 g_free ( auto_save_name );
2647 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2650 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2652 gchar *name_used = NULL;
2655 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2656 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2658 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2662 gchar *quoted_file = g_shell_quote ( name_used );
2663 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2664 g_free ( quoted_file );
2665 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2667 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2668 g_error_free ( err );
2672 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2673 //g_remove ( name_used );
2674 // Perhaps should be deleted when the program ends?
2675 // For now leave it to the user to delete it / use system temp cleanup methods.
2676 g_free ( name_used );
2680 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2682 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2685 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2687 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2690 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2692 gpointer layer_and_vlp[2];
2693 layer_and_vlp[0] = pass_along[0];
2694 layer_and_vlp[1] = pass_along[1];
2696 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2698 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2699 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2701 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2703 if ( !trk || !trk->name )
2706 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2707 gchar *auto_save_name = g_strdup ( trk->name );
2708 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2709 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2711 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2713 g_free ( auto_save_name );
2717 VikWaypoint *wp; // input
2718 gpointer uuid; // output
2721 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2723 wpu_udata *user_data = udata;
2724 if ( wp == user_data->wp ) {
2725 user_data->uuid = id;
2731 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2733 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2734 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2735 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2737 GTK_RESPONSE_REJECT,
2739 GTK_RESPONSE_ACCEPT,
2742 GtkWidget *label, *entry;
2743 label = gtk_label_new(_("Waypoint Name:"));
2744 entry = gtk_entry_new();
2746 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2747 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2748 gtk_widget_show_all ( label );
2749 gtk_widget_show_all ( entry );
2751 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2753 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2755 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2756 // Find *first* wp with the given name
2757 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2760 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2763 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2764 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2766 // Find and select on the side panel
2771 // Hmmm, want key of it
2772 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2774 if ( wpf && udata.uuid ) {
2775 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2776 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2785 gtk_widget_destroy ( dia );
2788 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2790 gchar *default_name = highest_wp_number_get(vtl);
2791 VikWaypoint *wp = vik_waypoint_new();
2792 gchar *returned_name;
2794 wp->coord = *def_coord;
2796 // Attempt to auto set height if DEM data is available
2797 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2798 if ( elev != VIK_DEM_INVALID_ELEVATION )
2799 wp->altitude = (gdouble)elev;
2801 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2803 if ( returned_name )
2806 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2807 g_free (default_name);
2808 g_free (returned_name);
2811 g_free (default_name);
2812 vik_waypoint_free(wp);
2816 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2819 struct LatLon one_ll, two_ll;
2820 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2822 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2823 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2824 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2825 VikViewport *vvp = vik_window_viewport(vw);
2826 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2827 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2828 vik_coord_to_latlon(&one, &one_ll);
2829 vik_coord_to_latlon(&two, &two_ll);
2830 if (one_ll.lat > two_ll.lat) {
2831 maxmin[0].lat = one_ll.lat;
2832 maxmin[1].lat = two_ll.lat;
2835 maxmin[0].lat = two_ll.lat;
2836 maxmin[1].lat = one_ll.lat;
2838 if (one_ll.lon > two_ll.lon) {
2839 maxmin[0].lon = one_ll.lon;
2840 maxmin[1].lon = two_ll.lon;
2843 maxmin[0].lon = two_ll.lon;
2844 maxmin[1].lon = one_ll.lon;
2846 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2849 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2851 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2852 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2853 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2855 trw_layer_find_maxmin (vtl, maxmin);
2856 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2859 #ifdef VIK_CONFIG_GEOTAG
2860 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2862 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2864 // Update directly - not changing the mtime
2865 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2868 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2870 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2873 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2877 * Use code in separate file for this feature as reasonably complex
2879 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2881 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2882 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2883 // Unset so can be reverified later if necessary
2884 vtl->has_verified_thumbnails = FALSE;
2886 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2892 static void trw_layer_geotagging ( gpointer lav[2] )
2894 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2895 // Unset so can be reverified later if necessary
2896 vtl->has_verified_thumbnails = FALSE;
2898 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2905 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2908 * Acquire into this TRW Layer straight from GPS Device
2910 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2912 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2913 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2914 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2915 VikViewport *vvp = vik_window_viewport(vw);
2917 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2918 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2921 #ifdef VIK_CONFIG_GOOGLE
2923 * Acquire into this TRW Layer from Google Directions
2925 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2927 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2928 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2929 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2930 VikViewport *vvp = vik_window_viewport(vw);
2932 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2936 #ifdef VIK_CONFIG_OPENSTREETMAP
2938 * Acquire into this TRW Layer from OSM
2940 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2942 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2943 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2944 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2945 VikViewport *vvp = vik_window_viewport(vw);
2947 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2951 #ifdef VIK_CONFIG_GEOCACHES
2953 * Acquire into this TRW Layer from Geocaching.com
2955 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2957 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2958 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2959 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2960 VikViewport *vvp = vik_window_viewport(vw);
2962 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2966 #ifdef VIK_CONFIG_GEOTAG
2968 * Acquire into this TRW Layer from images
2970 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2972 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2973 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2974 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2975 VikViewport *vvp = vik_window_viewport(vw);
2977 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2978 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2980 // Reverify thumbnails as they may have changed
2981 vtl->has_verified_thumbnails = FALSE;
2982 trw_layer_verify_thumbnails ( vtl, NULL );
2986 static void trw_layer_gps_upload ( gpointer lav[2] )
2988 gpointer pass_along[6];
2989 pass_along[0] = lav[0];
2990 pass_along[1] = lav[1];
2991 pass_along[2] = NULL; // No track - operate on the layer
2992 pass_along[3] = NULL;
2993 pass_along[4] = NULL;
2994 pass_along[5] = NULL;
2996 trw_layer_gps_upload_any ( pass_along );
3000 * If pass_along[3] is defined that this will upload just that track
3002 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3004 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3005 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3007 // May not actually get a track here as pass_along[2&3] can be null
3008 VikTrack *track = NULL;
3009 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3010 gboolean xfer_all = FALSE;
3012 if ( pass_along[2] ) {
3014 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3015 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3018 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3019 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3022 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3025 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3029 else if ( !pass_along[4] )
3030 xfer_all = TRUE; // i.e. whole layer
3032 if (track && !track->visible) {
3033 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3037 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3038 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3039 GTK_DIALOG_DESTROY_WITH_PARENT,
3041 GTK_RESPONSE_ACCEPT,
3043 GTK_RESPONSE_REJECT,
3046 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3047 GtkWidget *response_w = NULL;
3048 #if GTK_CHECK_VERSION (2, 20, 0)
3049 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3053 gtk_widget_grab_focus ( response_w );
3055 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3057 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3058 datasource_gps_clean_up ( dgs );
3059 gtk_widget_destroy ( dialog );
3063 // Get info from reused datasource dialog widgets
3064 gchar* protocol = datasource_gps_get_protocol ( dgs );
3065 gchar* port = datasource_gps_get_descriptor ( dgs );
3066 // NB don't free the above strings as they're references to values held elsewhere
3067 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3068 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3069 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3070 gboolean turn_off = datasource_gps_get_off ( dgs );
3072 gtk_widget_destroy ( dialog );
3074 // When called from the viewport - work the corresponding layerspanel:
3076 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3079 // Apply settings to transfer to the GPS device
3086 vik_layers_panel_get_viewport (vlp),
3095 * Acquire into this TRW Layer from any GPS Babel supported file
3097 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3099 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3100 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3101 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3102 VikViewport *vvp = vik_window_viewport(vw);
3104 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3107 static void trw_layer_new_wp ( gpointer lav[2] )
3109 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3110 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3111 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3112 instead return true if you want to update. */
3113 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 )
3114 vik_layers_panel_emit_update ( vlp );
3117 static void trw_layer_new_track ( gpointer lav[2] )
3119 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3121 if ( ! vtl->current_track ) {
3122 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3123 vtl->current_track = vik_track_new();
3124 vtl->current_track->visible = TRUE;
3125 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3127 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3131 static void trw_layer_new_route ( gpointer lav[2] )
3133 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3135 if ( ! vtl->current_track ) {
3136 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3137 vtl->current_track = vik_track_new();
3138 vtl->current_track->visible = TRUE;
3139 vtl->current_track->is_route = TRUE;
3140 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3142 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3146 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3148 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3149 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3151 if ( g_hash_table_size (vtl->routes) > 0 ) {
3152 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3153 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3154 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3155 vik_layers_panel_emit_update ( vlp );
3160 static void trw_layer_finish_track ( gpointer lav[2] )
3162 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3163 vtl->current_track = NULL;
3164 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3167 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3169 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3170 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3172 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3173 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3174 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3175 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3176 vik_layers_panel_emit_update ( vlp );
3180 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
3182 /* NB do not care if wp is visible or not */
3183 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3186 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3188 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3189 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3191 /* Only 1 waypoint - jump straight to it */
3192 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3193 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3194 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3196 /* If at least 2 waypoints - find center and then zoom to fit */
3197 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3199 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3200 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3201 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3204 vik_layers_panel_emit_update ( vlp );
3207 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3209 static gpointer pass_along[2];
3211 GtkWidget *export_submenu;
3212 pass_along[0] = vtl;
3213 pass_along[1] = vlp;
3215 item = gtk_menu_item_new();
3216 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3217 gtk_widget_show ( item );
3219 if ( vtl->current_track ) {
3220 if ( vtl->current_track->is_route )
3221 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3223 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3225 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3226 gtk_widget_show ( item );
3229 item = gtk_menu_item_new ();
3230 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3231 gtk_widget_show ( item );
3234 /* Now with icons */
3235 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3236 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3237 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3238 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3239 gtk_widget_show ( item );
3241 GtkWidget *view_submenu = gtk_menu_new();
3242 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3243 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3244 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3245 gtk_widget_show ( item );
3246 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3248 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3249 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3250 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3251 gtk_widget_show ( item );
3253 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3254 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3255 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3256 gtk_widget_show ( item );
3258 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3259 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3260 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3261 gtk_widget_show ( item );
3263 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3264 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3267 gtk_widget_show ( item );
3269 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3271 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3272 gtk_widget_show ( item );
3274 export_submenu = gtk_menu_new ();
3275 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3276 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3277 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3278 gtk_widget_show ( item );
3279 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3281 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3282 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3283 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3284 gtk_widget_show ( item );
3286 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3287 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3288 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3289 gtk_widget_show ( item );
3291 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3292 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3293 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3294 gtk_widget_show ( item );
3296 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3297 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3298 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3299 gtk_widget_show ( item );
3301 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3302 item = gtk_menu_item_new_with_mnemonic ( external1 );
3303 g_free ( external1 );
3304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3305 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3306 gtk_widget_show ( item );
3308 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3309 item = gtk_menu_item_new_with_mnemonic ( external2 );
3310 g_free ( external2 );
3311 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3312 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3313 gtk_widget_show ( item );
3315 GtkWidget *new_submenu = gtk_menu_new();
3316 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3317 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3318 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3319 gtk_widget_show(item);
3320 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3322 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3323 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3324 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3325 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3326 gtk_widget_show ( item );
3328 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3329 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3330 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3331 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3332 gtk_widget_show ( item );
3333 // Make it available only when a new track *not* already in progress
3334 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3336 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3337 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3338 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3339 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3340 gtk_widget_show ( item );
3341 // Make it available only when a new track *not* already in progress
3342 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3344 #ifdef VIK_CONFIG_GEONAMES
3345 GtkWidget *wikipedia_submenu = gtk_menu_new();
3346 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3347 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3348 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3349 gtk_widget_show(item);
3350 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3352 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3353 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3354 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3355 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3356 gtk_widget_show ( item );
3358 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3359 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3360 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3361 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3362 gtk_widget_show ( item );
3365 #ifdef VIK_CONFIG_GEOTAG
3366 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3368 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3369 gtk_widget_show ( item );
3372 GtkWidget *acquire_submenu = gtk_menu_new ();
3373 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3374 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3375 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3376 gtk_widget_show ( item );
3377 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3379 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3380 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3381 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3382 gtk_widget_show ( item );
3384 #ifdef VIK_CONFIG_GOOGLE
3385 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3386 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3387 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3388 gtk_widget_show ( item );
3391 #ifdef VIK_CONFIG_OPENSTREETMAP
3392 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3394 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3395 gtk_widget_show ( item );
3398 #ifdef VIK_CONFIG_GEOCACHES
3399 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3400 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3401 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3402 gtk_widget_show ( item );
3405 #ifdef VIK_CONFIG_GEOTAG
3406 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3407 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3408 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3409 gtk_widget_show ( item );
3412 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3413 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3414 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3415 gtk_widget_show ( item );
3417 GtkWidget *upload_submenu = gtk_menu_new ();
3418 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3419 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3420 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3421 gtk_widget_show ( item );
3422 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3424 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3425 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3426 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3427 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3428 gtk_widget_show ( item );
3430 #ifdef VIK_CONFIG_OPENSTREETMAP
3431 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3432 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3433 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3434 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3435 gtk_widget_show ( item );
3438 GtkWidget *delete_submenu = gtk_menu_new ();
3439 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3440 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3441 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3442 gtk_widget_show ( item );
3443 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3445 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3446 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3448 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3449 gtk_widget_show ( item );
3451 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3452 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3453 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3454 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3455 gtk_widget_show ( item );
3457 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3458 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3459 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3460 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3461 gtk_widget_show ( item );
3463 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3464 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3465 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3466 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3467 gtk_widget_show ( item );
3469 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3470 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3472 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3473 gtk_widget_show ( item );
3476 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3477 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3479 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3480 gtk_widget_show ( item );
3484 // Fake Waypoint UUIDs vi simple increasing integer
3485 static guint wp_uuid = 0;
3487 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3491 vik_waypoint_set_name (wp, name);
3493 if ( VIK_LAYER(vtl)->realized )
3495 // Do we need to create the sublayer:
3496 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3497 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3500 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3502 // Visibility column always needed for waypoints
3503 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3504 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 );
3506 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 );
3508 // Actual setting of visibility dependent on the waypoint
3509 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3511 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3514 highest_wp_number_add_wp(vtl, name);
3515 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3519 // Fake Track UUIDs vi simple increasing integer
3520 static guint tr_uuid = 0;
3522 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3526 vik_track_set_name (t, name);
3528 if ( VIK_LAYER(vtl)->realized )
3530 // Do we need to create the sublayer:
3531 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3532 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3535 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3536 // Visibility column always needed for tracks
3537 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3538 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 );
3540 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 );
3542 // Actual setting of visibility dependent on the track
3543 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3545 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3548 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3552 // Fake Route UUIDs vi simple increasing integer
3553 static guint rt_uuid = 0;
3555 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3559 vik_track_set_name (t, name);
3561 if ( VIK_LAYER(vtl)->realized )
3563 // Do we need to create the sublayer:
3564 if ( g_hash_table_size (vtl->routes) == 0 ) {
3565 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3568 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3569 // Visibility column always needed for tracks
3570 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3571 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 );
3573 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 );
3575 // Actual setting of visibility dependent on the track
3576 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3578 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3581 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3585 /* to be called whenever a track has been deleted or may have been changed. */
3586 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3588 if (vtl->current_tp_track == trk )
3589 trw_layer_cancel_current_tp ( vtl, FALSE );
3592 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3595 gchar *newname = g_strdup(name);
3600 switch ( sublayer_type ) {
3601 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3602 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3604 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3605 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3608 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3611 // If found a name already in use try adding 1 to it and we try again
3613 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3615 newname = new_newname;
3618 } while ( id != NULL);
3623 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3625 // No more uniqueness of name forced when loading from a file
3626 // This now makes this function a little redunant as we just flow the parameters through
3627 vik_trw_layer_add_waypoint ( vtl, name, wp );
3630 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3632 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3633 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3634 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3635 vik_track_free ( tr );
3636 vtl->route_finder_append = FALSE; /* this means we have added it */
3639 // No more uniqueness of name forced when loading from a file
3641 vik_trw_layer_add_route ( vtl, name, tr );
3643 vik_trw_layer_add_track ( vtl, name, tr );
3645 if ( vtl->route_finder_check_added_track ) {
3646 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3647 vtl->route_finder_added_track = tr;
3652 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3654 *l = g_list_append(*l, id);
3658 * Move an item from one TRW layer to another TRW layer
3660 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3662 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3663 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3665 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3667 VikTrack *trk2 = vik_track_copy ( trk );
3668 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3669 vik_trw_layer_delete_track ( vtl_src, trk );
3672 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3673 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3675 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3677 VikTrack *trk2 = vik_track_copy ( trk );
3678 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3679 vik_trw_layer_delete_route ( vtl_src, trk );
3682 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3683 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3685 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3687 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3688 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3689 trw_layer_delete_waypoint ( vtl_src, wp );
3693 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3695 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3696 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3698 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3699 GList *items = NULL;
3702 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3703 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3705 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3706 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3708 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3709 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3714 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3715 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3717 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3718 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3720 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3727 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3728 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3733 VikTrack *trk; // input
3734 gpointer uuid; // output
3737 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3739 trku_udata *user_data = udata;
3740 if ( trk == user_data->trk ) {
3741 user_data->uuid = id;
3747 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3749 gboolean was_visible = FALSE;
3751 if ( trk && trk->name ) {
3753 if ( trk == vtl->current_track ) {
3754 vtl->current_track = NULL;
3755 vtl->current_tp_track = NULL;
3756 vtl->current_tp_id = NULL;
3757 vtl->moving_tp = FALSE;
3760 was_visible = trk->visible;
3762 if ( trk == vtl->route_finder_current_track )
3763 vtl->route_finder_current_track = NULL;
3765 if ( trk == vtl->route_finder_added_track )
3766 vtl->route_finder_added_track = NULL;
3772 // Hmmm, want key of it
3773 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3775 if ( trkf && udata.uuid ) {
3776 /* could be current_tp, so we have to check */
3777 trw_layer_cancel_tps_of_track ( vtl, trk );
3779 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3782 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3783 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3784 g_hash_table_remove ( vtl->tracks, udata.uuid );
3786 // If last sublayer, then remove sublayer container
3787 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3788 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3796 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
3798 gboolean was_visible = FALSE;
3800 if ( trk && trk->name ) {
3802 if ( trk == vtl->current_track ) {
3803 vtl->current_track = NULL;
3804 vtl->current_tp_track = NULL;
3805 vtl->current_tp_id = NULL;
3806 vtl->moving_tp = FALSE;
3809 was_visible = trk->visible;
3811 if ( trk == vtl->route_finder_current_track )
3812 vtl->route_finder_current_track = NULL;
3814 if ( trk == vtl->route_finder_added_track )
3815 vtl->route_finder_added_track = NULL;
3821 // Hmmm, want key of it
3822 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
3824 if ( trkf && udata.uuid ) {
3825 /* could be current_tp, so we have to check */
3826 trw_layer_cancel_tps_of_track ( vtl, trk );
3828 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
3831 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3832 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
3833 g_hash_table_remove ( vtl->routes, udata.uuid );
3835 // If last sublayer, then remove sublayer container
3836 if ( g_hash_table_size (vtl->routes) == 0 ) {
3837 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
3845 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3847 gboolean was_visible = FALSE;
3849 if ( wp && wp->name ) {
3851 if ( wp == vtl->current_wp ) {
3852 vtl->current_wp = NULL;
3853 vtl->current_wp_id = NULL;
3854 vtl->moving_wp = FALSE;
3857 was_visible = wp->visible;
3863 // Hmmm, want key of it
3864 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3866 if ( wpf && udata.uuid ) {
3867 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3870 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3871 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3873 highest_wp_number_remove_wp(vtl, wp->name);
3874 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3876 // If last sublayer, then remove sublayer container
3877 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3878 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3888 // Only for temporary use by trw_layer_delete_waypoint_by_name
3889 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3891 wpu_udata *user_data = udata;
3892 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3893 user_data->uuid = id;
3900 * Delete a waypoint by the given name
3901 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3902 * as there be multiple waypoints with the same name
3904 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3907 // Fake a waypoint with the given name
3908 udata.wp = vik_waypoint_new ();
3909 vik_waypoint_set_name (udata.wp, name);
3910 // Currently only the name is used in this waypoint find function
3913 // Hmmm, want key of it
3914 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3916 vik_waypoint_free (udata.wp);
3918 if ( wpf && udata.uuid )
3919 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3925 VikTrack *trk; // input
3926 gpointer uuid; // output
3929 // Only for temporary use by trw_layer_delete_track_by_name
3930 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3932 tpu_udata *user_data = udata;
3933 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3934 user_data->uuid = id;
3941 * Delete a track by the given name
3942 * NOTE: ATM this will delete the first encountered Track with the specified name
3943 * as there may be multiple tracks with the same name within the specified hash table
3945 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
3948 // Fake a track with the given name
3949 udata.trk = vik_track_new ();
3950 vik_track_set_name (udata.trk, name);
3951 // Currently only the name is used in this waypoint find function
3954 // Hmmm, want key of it
3955 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3957 vik_track_free (udata.trk);
3959 if ( trkf && udata.uuid ) {
3960 // This could be a little better written...
3961 if ( vtl->tracks == ht_tracks )
3962 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
3963 if ( vtl->routes == ht_tracks )
3964 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
3971 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3973 vik_treeview_item_delete (vt, it );
3976 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
3979 vtl->current_track = NULL;
3980 vtl->route_finder_current_track = NULL;
3981 vtl->route_finder_added_track = NULL;
3982 if (vtl->current_tp_track)
3983 trw_layer_cancel_current_tp(vtl, FALSE);
3985 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3986 g_hash_table_remove_all(vtl->routes_iters);
3987 g_hash_table_remove_all(vtl->routes);
3989 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
3991 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3994 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3997 vtl->current_track = NULL;
3998 vtl->route_finder_current_track = NULL;
3999 vtl->route_finder_added_track = NULL;
4000 if (vtl->current_tp_track)
4001 trw_layer_cancel_current_tp(vtl, FALSE);
4003 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4004 g_hash_table_remove_all(vtl->tracks_iters);
4005 g_hash_table_remove_all(vtl->tracks);
4007 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4009 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4012 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4014 vtl->current_wp = NULL;
4015 vtl->current_wp_id = NULL;
4016 vtl->moving_wp = FALSE;
4018 highest_wp_number_reset(vtl);
4020 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4021 g_hash_table_remove_all(vtl->waypoints_iters);
4022 g_hash_table_remove_all(vtl->waypoints);
4024 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4026 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4029 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4031 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4032 // Get confirmation from the user
4033 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4034 _("Are you sure you want to delete all tracks in %s?"),
4035 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4036 vik_trw_layer_delete_all_tracks (vtl);
4039 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4041 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4042 // Get confirmation from the user
4043 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4044 _("Are you sure you want to delete all routes in %s?"),
4045 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4046 vik_trw_layer_delete_all_routes (vtl);
4049 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4051 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4052 // Get confirmation from the user
4053 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4054 _("Are you sure you want to delete all waypoints in %s?"),
4055 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4056 vik_trw_layer_delete_all_waypoints (vtl);
4059 static void trw_layer_delete_item ( gpointer pass_along[6] )
4061 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4062 gboolean was_visible = FALSE;
4063 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4065 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4066 if ( wp && wp->name ) {
4067 if ( GPOINTER_TO_INT ( pass_along[4]) )
4068 // Get confirmation from the user
4069 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4070 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4071 _("Are you sure you want to delete the waypoint \"%s\""),
4074 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4077 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4079 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4080 if ( trk && trk->name ) {
4081 if ( GPOINTER_TO_INT ( pass_along[4]) )
4082 // Get confirmation from the user
4083 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4084 _("Are you sure you want to delete the track \"%s\""),
4087 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4092 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4093 if ( trk && trk->name ) {
4094 if ( GPOINTER_TO_INT ( pass_along[4]) )
4095 // Get confirmation from the user
4096 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4097 _("Are you sure you want to delete the route \"%s\""),
4100 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4104 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4108 static void trw_layer_properties_item ( gpointer pass_along[7] )
4110 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4111 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4113 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4115 if ( wp && wp->name )
4117 gboolean updated = FALSE;
4118 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
4120 if ( updated && pass_along[6] )
4121 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4123 if ( updated && VIK_LAYER(vtl)->visible )
4124 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4130 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4131 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4133 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4135 if ( tr && tr->name )
4137 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4139 pass_along[1], /* vlp */
4140 pass_along[5] ); /* vvp */
4146 Parameter 1 -> VikLayersPanel
4147 Parameter 2 -> VikLayer
4148 Parameter 3 -> VikViewport
4150 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4153 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4154 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4157 /* since vlp not set, vl & vvp should be valid instead! */
4159 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4160 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
4165 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4167 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4169 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4170 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4172 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4174 if ( track && track->trackpoints )
4175 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4178 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4180 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4182 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4183 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4185 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4187 if ( track && track->trackpoints )
4189 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4191 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4192 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4193 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4194 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4195 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4199 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4201 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4203 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4204 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4206 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4211 // Converting a track to a route can be a bit more complicated,
4212 // so give a chance to change our minds:
4213 if ( !trk->is_route &&
4214 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4215 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4217 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4218 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4223 VikTrack *trk_copy = vik_track_copy ( trk );
4226 trk_copy->is_route = !trk_copy->is_route;
4228 // ATM can't set name to self - so must create temporary copy
4229 gchar *name = g_strdup ( trk_copy->name );
4231 // Delete old one and then add new one
4232 if ( trk->is_route ) {
4233 vik_trw_layer_delete_route ( vtl, trk );
4234 vik_trw_layer_add_track ( vtl, name, trk_copy );
4237 // Extra route conversion bits...
4238 vik_track_merge_segments ( trk_copy );
4239 vik_track_to_routepoints ( trk_copy );
4241 vik_trw_layer_delete_track ( vtl, trk );
4242 vik_trw_layer_add_route ( vtl, name, trk_copy );
4246 // Update in case color of track / route changes when moving between sublayers
4247 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4251 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4253 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4255 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4256 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4258 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4263 vtl->current_track = track;
4264 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);
4266 if ( track->trackpoints )
4267 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4270 #ifdef VIK_CONFIG_GOOGLE
4272 * extend a track using route finder
4274 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4276 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4277 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4278 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4280 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
4281 vtl->route_finder_coord = last_coord;
4282 vtl->route_finder_current_track = track;
4283 vtl->route_finder_started = TRUE;
4285 if ( track->trackpoints )
4286 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4291 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4293 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4294 /* Also warn if overwrite old elevation data */
4295 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4297 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4298 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4300 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4303 vik_track_apply_dem_data ( track );
4306 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4308 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4310 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4311 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4313 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4318 GList *trps = track->trackpoints;
4321 trps = g_list_last(trps);
4322 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4325 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4327 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4329 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4330 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4332 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4337 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4340 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4343 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4345 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4347 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4348 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4350 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4355 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4358 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4361 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4363 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4365 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4366 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4368 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4373 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4376 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4380 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4382 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4384 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4386 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4387 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4389 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4391 if ( trk && trk->trackpoints )
4393 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4394 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4395 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4396 if ( pass_along[1] )
4397 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4399 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4403 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4405 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4406 trw_layer_tpwin_init ( vtl );
4409 /*************************************
4410 * merge/split by time routines
4411 *************************************/
4413 /* called for each key in track hash table.
4414 * If the current track has the same time stamp type, add it to the result,
4415 * except the one pointed by "exclude".
4416 * set exclude to NULL if there is no exclude to check.
4417 * Note that the result is in reverse (for performance reasons).
4422 gboolean with_timestamps;
4424 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4426 twt_udata *user_data = udata;
4427 VikTrackpoint *p1, *p2;
4429 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4433 if (VIK_TRACK(value)->trackpoints) {
4434 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4435 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4437 if ( user_data->with_timestamps ) {
4438 if (!p1->has_timestamp || !p2->has_timestamp) {
4443 // Don't add tracks with timestamps when getting non timestamp tracks
4444 if (p1->has_timestamp || p2->has_timestamp) {
4450 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4453 /* called for each key in track hash table. if original track user_data[1] is close enough
4454 * to the passed one, add it to list in user_data[0]
4456 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4459 VikTrackpoint *p1, *p2;
4460 VikTrack *trk = VIK_TRACK(value);
4462 GList **nearby_tracks = ((gpointer *)user_data)[0];
4463 GList *tpoints = ((gpointer *)user_data)[1];
4466 * detect reasons for not merging, and return
4467 * if no reason is found not to merge, then do it.
4470 // Exclude the original track from the compiled list
4471 if (trk->trackpoints == tpoints) {
4475 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4476 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4478 if (trk->trackpoints) {
4479 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4480 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4482 if (!p1->has_timestamp || !p2->has_timestamp) {
4483 //g_print("no timestamp\n");
4487 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4488 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4489 if (! (abs(t1 - p2->timestamp) < threshold ||
4491 abs(p1->timestamp - t2) < threshold)
4498 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4501 /* comparison function used to sort tracks; a and b are hash table keys */
4502 /* Not actively used - can be restored if needed
4503 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4505 GHashTable *tracks = user_data;
4508 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4509 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4511 if (t1 < t2) return -1;
4512 if (t1 > t2) return 1;
4517 /* comparison function used to sort trackpoints */
4518 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4520 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4522 if (t1 < t2) return -1;
4523 if (t1 > t2) return 1;
4528 * comparison function which can be used to sort tracks or waypoints by name
4530 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4532 const gchar* namea = (const gchar*) a;
4533 const gchar* nameb = (const gchar*) b;
4534 if ( namea == NULL || nameb == NULL)
4537 // Same sort method as used in the vik_treeview_*_alphabetize functions
4538 return strcmp ( namea, nameb );
4542 * Attempt to merge selected track with other tracks specified by the user
4543 * Tracks to merge with must be of the same 'type' as the selected track -
4544 * either all with timestamps, or all without timestamps
4546 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4548 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4549 GList *other_tracks = NULL;
4550 GHashTable *ght_tracks;
4551 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4552 ght_tracks = vtl->routes;
4554 ght_tracks = vtl->tracks;
4556 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4561 if ( !track->trackpoints )
4565 udata.result = &other_tracks;
4566 udata.exclude = track->trackpoints;
4567 // Allow merging with 'similar' time type time tracks
4568 // i.e. either those times, or those without
4569 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4571 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4572 other_tracks = g_list_reverse(other_tracks);
4574 if ( !other_tracks ) {
4575 if ( udata.with_timestamps )
4576 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4578 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4582 // Sort alphabetically for user presentation
4583 // Convert into list of names for usage with dialog function
4584 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4585 GList *other_tracks_names = NULL;
4586 GList *iter = g_list_first ( other_tracks );
4588 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4589 iter = g_list_next ( iter );
4592 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4594 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4598 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4599 g_list_free(other_tracks);
4600 g_list_free(other_tracks_names);
4605 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4606 VikTrack *merge_track;
4607 if ( track->is_route )
4608 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4610 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4613 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4614 merge_track->trackpoints = NULL;
4615 if ( track->is_route )
4616 vik_trw_layer_delete_route (vtl, merge_track);
4618 vik_trw_layer_delete_track (vtl, merge_track);
4619 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4622 /* TODO: free data before free merge_list */
4623 for (l = merge_list; l != NULL; l = g_list_next(l))
4625 g_list_free(merge_list);
4626 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4630 // c.f. trw_layer_sorted_track_id_by_name_list
4631 // but don't add the specified track to the list (normally current track)
4632 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4634 twt_udata *user_data = udata;
4637 if (trk->trackpoints == user_data->exclude) {
4641 // Sort named list alphabetically
4642 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4646 * Join - this allows combining 'tracks' and 'track routes'
4647 * i.e. doesn't care about whether tracks have consistent timestamps
4648 * ATM can only append one track at a time to the currently selected track
4650 static void trw_layer_append_track ( gpointer pass_along[6] )
4653 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4655 GHashTable *ght_tracks;
4656 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4657 ght_tracks = vtl->routes;
4659 ght_tracks = vtl->tracks;
4661 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4666 GList *other_tracks_names = NULL;
4668 // Sort alphabetically for user presentation
4669 // Convert into list of names for usage with dialog function
4670 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4672 udata.result = &other_tracks_names;
4673 udata.exclude = trk->trackpoints;
4675 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4677 // Note the limit to selecting one track only
4678 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4679 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4680 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4683 trk->is_route ? _("Append Route"): _("Append Track"),
4684 trk->is_route ? _("Select the route to append after the current route") :
4685 _("Select the track to append after the current track") );
4687 g_list_free(other_tracks_names);
4689 // It's a list, but shouldn't contain more than one other track!
4690 if ( append_list ) {
4692 for (l = append_list; l != NULL; l = g_list_next(l)) {
4693 // TODO: at present this uses the first track found by name,
4694 // which with potential multiple same named tracks may not be the one selected...
4695 VikTrack *append_track;
4696 if ( trk->is_route )
4697 append_track = vik_trw_layer_get_route ( vtl, l->data );
4699 append_track = vik_trw_layer_get_track ( vtl, l->data );
4701 if ( append_track ) {
4702 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4703 append_track->trackpoints = NULL;
4704 if ( trk->is_route )
4705 vik_trw_layer_delete_route (vtl, append_track);
4707 vik_trw_layer_delete_track (vtl, append_track);
4710 for (l = append_list; l != NULL; l = g_list_next(l))
4712 g_list_free(append_list);
4713 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4718 * Very similar to trw_layer_append_track for joining
4719 * but this allows selection from the 'other' list
4720 * If a track is selected, then is shows routes and joins the selected one
4721 * If a route is selected, then is shows tracks and joins the selected one
4723 static void trw_layer_append_other ( gpointer pass_along[6] )
4726 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4728 GHashTable *ght_mykind, *ght_others;
4729 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4730 ght_mykind = vtl->routes;
4731 ght_others = vtl->tracks;
4734 ght_mykind = vtl->tracks;
4735 ght_others = vtl->routes;
4738 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
4743 GList *other_tracks_names = NULL;
4745 // Sort alphabetically for user presentation
4746 // Convert into list of names for usage with dialog function
4747 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4749 udata.result = &other_tracks_names;
4750 udata.exclude = trk->trackpoints;
4752 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4754 // Note the limit to selecting one track only
4755 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4756 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4757 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4760 trk->is_route ? _("Append Track"): _("Append Route"),
4761 trk->is_route ? _("Select the track to append after the current route") :
4762 _("Select the route to append after the current track") );
4764 g_list_free(other_tracks_names);
4766 // It's a list, but shouldn't contain more than one other track!
4767 if ( append_list ) {
4769 for (l = append_list; l != NULL; l = g_list_next(l)) {
4770 // TODO: at present this uses the first track found by name,
4771 // which with potential multiple same named tracks may not be the one selected...
4773 // Get FROM THE OTHER TYPE list
4774 VikTrack *append_track;
4775 if ( trk->is_route )
4776 append_track = vik_trw_layer_get_track ( vtl, l->data );
4778 append_track = vik_trw_layer_get_route ( vtl, l->data );
4780 if ( append_track ) {
4782 if ( !append_track->is_route &&
4783 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
4784 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
4786 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4787 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
4788 vik_track_merge_segments ( append_track );
4789 vik_track_to_routepoints ( append_track );
4796 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4797 append_track->trackpoints = NULL;
4799 // Delete copied which is FROM THE OTHER TYPE list
4800 if ( trk->is_route )
4801 vik_trw_layer_delete_track (vtl, append_track);
4803 vik_trw_layer_delete_route (vtl, append_track);
4806 for (l = append_list; l != NULL; l = g_list_next(l))
4808 g_list_free(append_list);
4809 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4813 /* merge by segments */
4814 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4816 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4817 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4818 guint segments = vik_track_merge_segments ( trk );
4819 // NB currently no need to redraw as segments not actually shown on the display
4820 // However inform the user of what happened:
4822 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4823 g_snprintf(str, 64, tmp_str, segments);
4824 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4827 /* merge by time routine */
4828 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4830 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4834 GList *tracks_with_timestamp = NULL;
4835 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4836 if (orig_trk->trackpoints &&
4837 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4838 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4843 udata.result = &tracks_with_timestamp;
4844 udata.exclude = orig_trk->trackpoints;
4845 udata.with_timestamps = TRUE;
4846 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4847 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4849 if (!tracks_with_timestamp) {
4850 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4853 g_list_free(tracks_with_timestamp);
4855 static guint threshold_in_minutes = 1;
4856 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4857 _("Merge Threshold..."),
4858 _("Merge when time between tracks less than:"),
4859 &threshold_in_minutes)) {
4863 // keep attempting to merge all tracks until no merges within the time specified is possible
4864 gboolean attempt_merge = TRUE;
4865 GList *nearby_tracks = NULL;
4867 static gpointer params[3];
4869 while ( attempt_merge ) {
4871 // Don't try again unless tracks have changed
4872 attempt_merge = FALSE;
4874 trps = orig_trk->trackpoints;
4878 if (nearby_tracks) {
4879 g_list_free(nearby_tracks);
4880 nearby_tracks = NULL;
4883 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4884 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4886 /* g_print("Original track times: %d and %d\n", t1, t2); */
4887 params[0] = &nearby_tracks;
4888 params[1] = (gpointer)trps;
4889 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4891 /* get a list of adjacent-in-time tracks */
4892 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4895 GList *l = nearby_tracks;
4898 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4899 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4901 t1 = get_first_trackpoint(l)->timestamp;
4902 t2 = get_last_trackpoint(l)->timestamp;
4903 #undef get_first_trackpoint
4904 #undef get_last_trackpoint
4905 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4908 /* remove trackpoints from merged track, delete track */
4909 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4910 VIK_TRACK(l->data)->trackpoints = NULL;
4911 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4913 // Tracks have changed, therefore retry again against all the remaining tracks
4914 attempt_merge = TRUE;
4919 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
4922 g_list_free(nearby_tracks);
4923 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4927 * Split a track at the currently selected trackpoint
4929 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
4931 if ( !vtl->current_tpl )
4934 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
4935 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
4937 VikTrack *tr = vik_track_new ();
4938 GList *newglist = g_list_alloc ();
4939 newglist->prev = NULL;
4940 newglist->next = vtl->current_tpl->next;
4941 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4942 tr->trackpoints = newglist;
4943 tr->is_route = vtl->current_tp_track->is_route;
4946 vtl->current_tpl->next->prev = newglist; /* end old track here */
4947 vtl->current_tpl->next = NULL;
4949 vtl->current_tpl = newglist; /* change tp to first of new track. */
4950 vtl->current_tp_track = tr;
4953 vik_trw_layer_add_route ( vtl, name, tr );
4955 vik_trw_layer_add_track ( vtl, name, tr );
4961 // Also need id of newly created track
4964 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4966 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4968 if ( trkf && udata.uuid )
4969 vtl->current_tp_id = udata.uuid;
4971 vtl->current_tp_id = NULL;
4973 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4978 /* split by time routine */
4979 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
4981 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4982 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4983 GList *trps = track->trackpoints;
4985 GList *newlists = NULL;
4986 GList *newtps = NULL;
4987 static guint thr = 1;
4994 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4995 _("Split Threshold..."),
4996 _("Split when time between trackpoints exceeds:"),
5001 /* iterate through trackpoints, and copy them into new lists without touching original list */
5002 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5006 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5008 g_print("panic: ts < prev_ts: this should never happen!\n");
5011 if (ts - prev_ts > thr*60) {
5012 /* flush accumulated trackpoints into new list */
5013 newlists = g_list_append(newlists, g_list_reverse(newtps));
5017 /* accumulate trackpoint copies in newtps, in reverse order */
5018 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5020 iter = g_list_next(iter);
5023 newlists = g_list_append(newlists, g_list_reverse(newtps));
5026 /* put lists of trackpoints into tracks */
5028 // Only bother updating if the split results in new tracks
5029 if (g_list_length (newlists) > 1) {
5034 tr = vik_track_new();
5035 tr->visible = track->visible;
5036 tr->trackpoints = (GList *)(iter->data);
5038 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5039 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5040 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5041 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5043 iter = g_list_next(iter);
5045 // Remove original track and then update the display
5046 vik_trw_layer_delete_track (vtl, track);
5047 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
5049 g_list_free(newlists);
5053 * Split a track by the number of points as specified by the user
5055 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5057 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5059 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5060 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5062 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5067 // Check valid track
5068 GList *trps = track->trackpoints;
5072 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5073 _("Split Every Nth Point"),
5074 _("Split on every Nth point:"),
5075 250, // Default value as per typical limited track capacity of various GPS devices
5079 // Was a valid number returned?
5085 GList *newlists = NULL;
5086 GList *newtps = NULL;
5091 /* accumulate trackpoint copies in newtps, in reverse order */
5092 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5094 if (count >= points) {
5095 /* flush accumulated trackpoints into new list */
5096 newlists = g_list_append(newlists, g_list_reverse(newtps));
5100 iter = g_list_next(iter);
5103 // If there is a remaining chunk put that into the new split list
5104 // This may well be the whole track if no split points were encountered
5106 newlists = g_list_append(newlists, g_list_reverse(newtps));
5109 /* put lists of trackpoints into tracks */
5111 // Only bother updating if the split results in new tracks
5112 if (g_list_length (newlists) > 1) {
5117 tr = vik_track_new();
5118 tr->visible = track->visible;
5119 tr->is_route = track->is_route;
5120 tr->trackpoints = (GList *)(iter->data);
5122 if ( track->is_route ) {
5123 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5124 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5127 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5128 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5130 iter = g_list_next(iter);
5132 // Remove original track and then update the display
5133 if ( track->is_route )
5134 vik_trw_layer_delete_route (vtl, track);
5136 vik_trw_layer_delete_track (vtl, track);
5137 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
5139 g_list_free(newlists);
5143 * Split a track at the currently selected trackpoint
5145 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5147 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5148 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5149 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5153 * Split a track by its segments
5154 * Routes do not have segments so don't call this for routes
5156 static void trw_layer_split_segments ( gpointer pass_along[6] )
5158 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5159 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5166 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5169 for ( i = 0; i < ntracks; i++ ) {
5171 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5172 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5177 // Remove original track
5178 vik_trw_layer_delete_track ( vtl, trk );
5179 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5182 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5185 /* end of split/merge routines */
5188 * Delete adjacent track points at the same position
5189 * AKA Delete Dulplicates on the Properties Window
5191 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5193 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5195 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5196 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5198 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5203 gulong removed = vik_track_remove_dup_points ( trk );
5205 // Track has been updated so update tps:
5206 trw_layer_cancel_tps_of_track ( vtl, trk );
5208 // Inform user how much was deleted as it's not obvious from the normal view
5210 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5211 g_snprintf(str, 64, tmp_str, removed);
5212 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5214 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5218 * Delete adjacent track points with the same timestamp
5219 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5221 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5223 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5225 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5226 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5228 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5233 gulong removed = vik_track_remove_same_time_points ( trk );
5235 // Track has been updated so update tps:
5236 trw_layer_cancel_tps_of_track ( vtl, trk );
5238 // Inform user how much was deleted as it's not obvious from the normal view
5240 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5241 g_snprintf(str, 64, tmp_str, removed);
5242 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5244 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5250 static void trw_layer_reverse ( gpointer pass_along[6] )
5252 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5254 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5255 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5257 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5262 // Check valid track
5263 GList *trps = track->trackpoints;
5267 vik_track_reverse ( track );
5269 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
5273 * Similar to trw_layer_enum_item, but this uses a sorted method
5276 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5278 GList **list = (GList**)udata;
5279 // *list = g_list_prepend(*all, key); //unsorted method
5280 // Sort named list alphabetically
5281 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5286 * Now Waypoint specific sort
5288 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5290 GList **list = (GList**)udata;
5291 // Sort named list alphabetically
5292 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5296 * Track specific sort
5298 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5300 GList **list = (GList**)udata;
5301 // Sort named list alphabetically
5302 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5307 gboolean has_same_track_name;
5308 const gchar *same_track_name;
5309 } same_track_name_udata;
5311 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5313 const gchar* namea = (const gchar*) aa;
5314 const gchar* nameb = (const gchar*) bb;
5317 gint result = strcmp ( namea, nameb );
5319 if ( result == 0 ) {
5320 // Found two names the same
5321 same_track_name_udata *user_data = udata;
5322 user_data->has_same_track_name = TRUE;
5323 user_data->same_track_name = namea;
5326 // Leave ordering the same
5331 * Find out if any tracks have the same name in this hash table
5333 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5335 // Sort items by name, then compare if any next to each other are the same
5337 GList *track_names = NULL;
5338 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5341 if ( ! track_names )
5344 same_track_name_udata udata;
5345 udata.has_same_track_name = FALSE;
5347 // Use sort routine to traverse list comparing items
5348 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5349 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5350 // Still no tracks...
5354 return udata.has_same_track_name;
5358 * Force unqiue track names for the track table specified
5359 * Note the panel is a required parameter to enable the update of the names displayed
5360 * Specify if on tracks or else on routes
5362 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5364 // . Search list for an instance of repeated name
5365 // . get track of this name
5366 // . create new name
5367 // . rename track & update equiv. treeview iter
5368 // . repeat until all different
5370 same_track_name_udata udata;
5372 GList *track_names = NULL;
5373 udata.has_same_track_name = FALSE;
5374 udata.same_track_name = NULL;
5376 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5379 if ( ! track_names )
5382 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5384 // Still no tracks...
5385 if ( ! dummy_list1 )
5388 while ( udata.has_same_track_name ) {
5390 // Find a track with the same name
5393 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5395 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5399 g_critical("Houston, we've had a problem.");
5400 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5401 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5406 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5407 vik_track_set_name ( trk, newname );
5413 // Need want key of it for treeview update
5414 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5416 if ( trkf && udataU.uuid ) {
5420 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5422 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5425 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5426 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5427 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5432 // Start trying to find same names again...
5434 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5435 udata.has_same_track_name = FALSE;
5436 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5438 // No tracks any more - give up searching
5439 if ( ! dummy_list2 )
5440 udata.has_same_track_name = FALSE;
5444 vik_layers_panel_emit_update ( vlp );
5450 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5452 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5455 // Ensure list of track names offered is unique
5456 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5457 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5458 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5459 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5465 // Sort list alphabetically for better presentation
5466 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5469 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5473 // Get list of items to delete from the user
5474 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5477 _("Delete Selection"),
5478 _("Select tracks to delete"));
5481 // Delete requested tracks
5482 // since specificly requested, IMHO no need for extra confirmation
5483 if ( delete_list ) {
5485 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5486 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5487 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5489 g_list_free(delete_list);
5490 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5497 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5499 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5502 // Ensure list of track names offered is unique
5503 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5504 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5505 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5506 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5512 // Sort list alphabetically for better presentation
5513 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5516 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5520 // Get list of items to delete from the user
5521 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5524 _("Delete Selection"),
5525 _("Select routes to delete") );
5528 // Delete requested routes
5529 // since specificly requested, IMHO no need for extra confirmation
5530 if ( delete_list ) {
5532 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5533 // This deletes first route it finds of that name (but uniqueness is enforced above)
5534 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5536 g_list_free(delete_list);
5537 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5542 gboolean has_same_waypoint_name;
5543 const gchar *same_waypoint_name;
5544 } same_waypoint_name_udata;
5546 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5548 const gchar* namea = (const gchar*) aa;
5549 const gchar* nameb = (const gchar*) bb;
5552 gint result = strcmp ( namea, nameb );
5554 if ( result == 0 ) {
5555 // Found two names the same
5556 same_waypoint_name_udata *user_data = udata;
5557 user_data->has_same_waypoint_name = TRUE;
5558 user_data->same_waypoint_name = namea;
5561 // Leave ordering the same
5566 * Find out if any waypoints have the same name in this layer
5568 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5570 // Sort items by name, then compare if any next to each other are the same
5572 GList *waypoint_names = NULL;
5573 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5576 if ( ! waypoint_names )
5579 same_waypoint_name_udata udata;
5580 udata.has_same_waypoint_name = FALSE;
5582 // Use sort routine to traverse list comparing items
5583 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5584 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5585 // Still no waypoints...
5589 return udata.has_same_waypoint_name;
5593 * Force unqiue waypoint names for this layer
5594 * Note the panel is a required parameter to enable the update of the names displayed
5596 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5598 // . Search list for an instance of repeated name
5599 // . get waypoint of this name
5600 // . create new name
5601 // . rename waypoint & update equiv. treeview iter
5602 // . repeat until all different
5604 same_waypoint_name_udata udata;
5606 GList *waypoint_names = NULL;
5607 udata.has_same_waypoint_name = FALSE;
5608 udata.same_waypoint_name = NULL;
5610 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5613 if ( ! waypoint_names )
5616 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5618 // Still no waypoints...
5619 if ( ! dummy_list1 )
5622 while ( udata.has_same_waypoint_name ) {
5624 // Find a waypoint with the same name
5625 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5629 g_critical("Houston, we've had a problem.");
5630 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5631 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5636 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5637 vik_waypoint_set_name ( waypoint, newname );
5640 udataU.wp = waypoint;
5643 // Need want key of it for treeview update
5644 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5646 if ( wpf && udataU.uuid ) {
5648 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5651 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5652 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5653 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5658 // Start trying to find same names again...
5659 waypoint_names = NULL;
5660 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5661 udata.has_same_waypoint_name = FALSE;
5662 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5664 // No waypoints any more - give up searching
5665 if ( ! dummy_list2 )
5666 udata.has_same_waypoint_name = FALSE;
5670 vik_layers_panel_emit_update ( vlp );
5676 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5678 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5681 // Ensure list of waypoint names offered is unique
5682 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5683 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5684 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5685 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5691 // Sort list alphabetically for better presentation
5692 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5694 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5698 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5700 // Get list of items to delete from the user
5701 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5704 _("Delete Selection"),
5705 _("Select waypoints to delete"));
5708 // Delete requested waypoints
5709 // since specificly requested, IMHO no need for extra confirmation
5710 if ( delete_list ) {
5712 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5713 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5714 trw_layer_delete_waypoint_by_name (vtl, l->data);
5716 g_list_free(delete_list);
5717 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5722 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5724 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5726 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5729 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5731 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
5732 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5736 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
5738 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5740 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
5742 // No actual change to the name supplied
5743 if (strcmp(newname, wp->name) == 0 )
5746 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5749 // An existing waypoint has been found with the requested name
5750 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5751 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
5756 // Update WP name and refresh the treeview
5757 vik_waypoint_set_name (wp, newname);
5759 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5760 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5763 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5768 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5770 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
5772 // No actual change to the name supplied
5773 if (strcmp(newname, trk->name) == 0)
5776 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
5779 // An existing track has been found with the requested name
5780 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5781 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
5785 // Update track name and refresh GUI parts
5786 vik_track_set_name (trk, newname);
5788 // Update any subwindows that could be displaying this track which has changed name
5789 // Only one Track Edit Window
5790 if ( l->current_tp_track == trk && l->tpwin ) {
5791 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5793 // Property Dialog of the track
5794 vik_trw_layer_propwin_update ( trk );
5796 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5797 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5800 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5805 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5807 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
5809 // No actual change to the name supplied
5810 if (strcmp(newname, trk->name) == 0)
5813 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
5816 // An existing track has been found with the requested name
5817 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5818 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
5822 // Update track name and refresh GUI parts
5823 vik_track_set_name (trk, newname);
5825 // Update any subwindows that could be displaying this track which has changed name
5826 // Only one Track Edit Window
5827 if ( l->current_tp_track == trk && l->tpwin ) {
5828 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5830 // Property Dialog of the track
5831 vik_trw_layer_propwin_update ( trk );
5833 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5834 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5837 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5844 static gboolean is_valid_geocache_name ( gchar *str )
5846 gint len = strlen ( str );
5847 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]));
5850 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
5852 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5853 a_acquire_set_filter_track ( trk );
5856 #ifdef VIK_CONFIG_GOOGLE
5857 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
5859 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
5860 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
5863 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
5865 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5867 gchar *escaped = uri_escape ( tr->comment );
5868 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
5869 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5876 /* vlp can be NULL if necessary - i.e. right-click from a tool */
5877 /* viewpoint is now available instead */
5878 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
5880 static gpointer pass_along[8];
5882 gboolean rv = FALSE;
5885 pass_along[1] = vlp;
5886 pass_along[2] = GINT_TO_POINTER (subtype);
5887 pass_along[3] = sublayer;
5888 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5889 pass_along[5] = vvp;
5890 pass_along[6] = iter;
5891 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
5893 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5897 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5898 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5899 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5900 gtk_widget_show ( item );
5902 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5903 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
5904 if (tr && tr->property_dialog)
5905 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5907 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
5908 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
5909 if (tr && tr->property_dialog)
5910 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5913 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5914 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5915 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5916 gtk_widget_show ( item );
5918 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
5919 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
5920 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5921 gtk_widget_show ( item );
5923 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
5924 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
5925 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5926 gtk_widget_show ( item );
5928 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5930 gboolean separator_created = FALSE;
5932 /* could be a right-click using the tool */
5933 if ( vlp != NULL ) {
5934 item = gtk_menu_item_new ();
5935 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5936 gtk_widget_show ( item );
5938 separator_created = TRUE;
5940 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5941 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5942 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
5943 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5944 gtk_widget_show ( item );
5947 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5949 if ( wp && wp->name ) {
5950 if ( is_valid_geocache_name ( wp->name ) ) {
5952 if ( !separator_created ) {
5953 item = gtk_menu_item_new ();
5954 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5955 gtk_widget_show ( item );
5956 separator_created = TRUE;
5959 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
5960 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
5961 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5962 gtk_widget_show ( item );
5966 if ( wp && wp->image )
5968 if ( !separator_created ) {
5969 item = gtk_menu_item_new ();
5970 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5971 gtk_widget_show ( item );
5972 separator_created = TRUE;
5975 // Set up image paramater
5976 pass_along[5] = wp->image;
5978 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
5979 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
5980 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
5981 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5982 gtk_widget_show ( item );
5984 #ifdef VIK_CONFIG_GEOTAG
5985 GtkWidget *geotag_submenu = gtk_menu_new ();
5986 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
5987 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
5988 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5989 gtk_widget_show ( item );
5990 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
5992 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
5993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
5994 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5995 gtk_widget_show ( item );
5997 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
5998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
5999 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6000 gtk_widget_show ( item );
6007 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6008 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6010 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6011 gtk_widget_show ( item );
6012 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6013 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6014 gtk_widget_set_sensitive ( item, TRUE );
6016 gtk_widget_set_sensitive ( item, FALSE );
6019 item = gtk_menu_item_new ();
6020 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6021 gtk_widget_show ( item );
6024 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6027 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6028 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6030 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6031 gtk_widget_show ( item );
6034 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6036 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6037 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6039 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6040 gtk_widget_show ( item );
6042 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6043 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6044 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6045 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6046 gtk_widget_show ( item );
6048 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6049 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6051 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6052 gtk_widget_show ( item );
6054 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6055 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6056 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6057 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6058 gtk_widget_show ( item );
6061 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6065 if ( l->current_track && !l->current_track->is_route ) {
6066 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6068 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6069 gtk_widget_show ( item );
6071 item = gtk_menu_item_new ();
6072 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6073 gtk_widget_show ( item );
6076 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6077 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6079 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6080 gtk_widget_show ( item );
6082 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6083 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6084 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6085 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6086 gtk_widget_show ( item );
6087 // Make it available only when a new track *not* already in progress
6088 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6090 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6091 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6092 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6093 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6094 gtk_widget_show ( item );
6096 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6097 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6098 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6099 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6100 gtk_widget_show ( item );
6103 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6107 if ( l->current_track && l->current_track->is_route ) {
6108 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6109 // Reuse finish track method
6110 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6111 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6112 gtk_widget_show ( item );
6114 item = gtk_menu_item_new ();
6115 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6116 gtk_widget_show ( item );
6119 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6120 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6122 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6123 gtk_widget_show ( item );
6125 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6126 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6128 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6129 gtk_widget_show ( item );
6130 // Make it available only when a new track *not* already in progress
6131 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6133 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6134 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6136 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6137 gtk_widget_show ( item );
6139 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6140 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6141 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6142 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6143 gtk_widget_show ( item );
6146 GtkWidget *upload_submenu = gtk_menu_new ();
6148 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6150 item = gtk_menu_item_new ();
6151 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6152 gtk_widget_show ( item );
6154 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6155 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6156 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6157 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6158 if ( l->current_track ) {
6159 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6160 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6161 gtk_widget_show ( item );
6164 item = gtk_menu_item_new ();
6165 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6166 gtk_widget_show ( item );
6169 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6170 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6172 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6173 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6175 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6176 gtk_widget_show ( item );
6178 GtkWidget *goto_submenu;
6179 goto_submenu = gtk_menu_new ();
6180 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6182 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6183 gtk_widget_show ( item );
6184 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6186 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6187 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6189 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6190 gtk_widget_show ( item );
6192 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6193 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6194 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6195 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6196 gtk_widget_show ( item );
6198 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6199 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6201 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6202 gtk_widget_show ( item );
6204 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6207 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6208 gtk_widget_show ( item );
6210 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6212 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6213 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6214 gtk_widget_show ( item );
6216 // Routes don't have speeds
6217 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6218 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6219 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6221 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6222 gtk_widget_show ( item );
6225 GtkWidget *combine_submenu;
6226 combine_submenu = gtk_menu_new ();
6227 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6228 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6229 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6230 gtk_widget_show ( item );
6231 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6233 // Routes don't have times or segments...
6234 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6235 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6236 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6237 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6238 gtk_widget_show ( item );
6240 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6241 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6242 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6243 gtk_widget_show ( item );
6246 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6247 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6248 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6249 gtk_widget_show ( item );
6251 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6252 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6254 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6256 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6257 gtk_widget_show ( item );
6259 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6260 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6262 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6264 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6265 gtk_widget_show ( item );
6267 GtkWidget *split_submenu;
6268 split_submenu = gtk_menu_new ();
6269 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6270 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6271 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6272 gtk_widget_show ( item );
6273 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6275 // Routes don't have times or segments...
6276 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6277 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6279 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6280 gtk_widget_show ( item );
6282 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6283 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6285 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6286 gtk_widget_show ( item );
6289 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6291 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6292 gtk_widget_show ( item );
6294 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6296 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6297 gtk_widget_show ( item );
6298 // Make it available only when a trackpoint is selected.
6299 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6301 GtkWidget *delete_submenu;
6302 delete_submenu = gtk_menu_new ();
6303 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6304 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6305 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6306 gtk_widget_show ( item );
6307 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6309 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6311 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6312 gtk_widget_show ( item );
6314 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6316 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6317 gtk_widget_show ( item );
6319 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6320 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6322 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6323 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6324 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6325 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6326 gtk_widget_show ( item );
6328 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6330 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6331 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6333 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6334 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
6335 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6336 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6337 gtk_widget_show ( item );
6340 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6341 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
6342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6343 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6344 gtk_widget_show ( item );
6346 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6347 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6349 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6350 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6351 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6352 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6353 gtk_widget_show ( item );
6355 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6356 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6358 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6359 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6360 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6361 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6362 gtk_widget_show ( item );
6364 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6365 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6367 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6368 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6369 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6370 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6371 gtk_widget_show ( item );
6373 #ifdef VIK_CONFIG_GOOGLE
6374 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6375 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
6376 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6377 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6378 gtk_widget_show ( item );
6381 // ATM can't upload a single waypoint but can do waypoints to a GPS
6382 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6383 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6384 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6385 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6386 gtk_widget_show ( item );
6387 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6389 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6390 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6391 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6392 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6393 gtk_widget_show ( item );
6397 // Some things aren't usable with routes
6398 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6399 #ifdef VIK_CONFIG_OPENSTREETMAP
6400 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6401 // Convert internal pointer into actual track for usage outside this file
6402 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6403 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6404 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6405 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6406 gtk_widget_show ( item );
6409 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6410 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6412 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6413 gtk_widget_show ( item );
6415 #ifdef VIK_CONFIG_GOOGLE
6416 if ( is_valid_google_route ( l, sublayer ) )
6418 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6419 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6420 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
6421 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6422 gtk_widget_show ( item );
6426 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6427 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6428 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6429 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6430 gtk_widget_show ( item );
6432 /* ATM This function is only available via the layers panel, due to needing a vlp */
6434 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6435 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6436 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6438 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6439 gtk_widget_show ( item );
6443 #ifdef VIK_CONFIG_GEOTAG
6444 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6445 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6446 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6447 gtk_widget_show ( item );
6451 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6452 // Only show on viewport popmenu when a trackpoint is selected
6453 if ( ! vlp && l->current_tpl ) {
6455 item = gtk_menu_item_new ();
6456 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6457 gtk_widget_show ( item );
6459 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6460 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6461 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6462 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6463 gtk_widget_show ( item );
6470 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6473 if (!vtl->current_tpl)
6475 if (!vtl->current_tpl->next)
6478 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6479 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6481 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6484 VikTrackpoint *tp_new = vik_trackpoint_new();
6485 struct LatLon ll_current, ll_next;
6486 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6487 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6489 /* main positional interpolation */
6490 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6491 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6493 /* Now other properties that can be interpolated */
6494 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6496 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6497 /* Note here the division is applied to each part, then added
6498 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6499 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6500 tp_new->has_timestamp = TRUE;
6503 if (tp_current->speed != NAN && tp_next->speed != NAN)
6504 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6506 /* TODO - improve interpolation of course, as it may not be correct.
6507 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6508 [similar applies if value is in radians] */
6509 if (tp_current->course != NAN && tp_next->course != NAN)
6510 tp_new->speed = (tp_current->course + tp_next->course)/2;
6512 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6514 /* Insert new point into the trackpoints list after the current TP */
6515 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6517 // Otherwise try routes
6518 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6522 gint index = g_list_index ( trk->trackpoints, tp_current );
6524 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6529 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6535 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6539 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6541 if ( vtl->current_tpl )
6543 vtl->current_tpl = NULL;
6544 vtl->current_tp_track = NULL;
6545 vtl->current_tp_id = NULL;
6546 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6550 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6552 g_assert ( vtl->tpwin != NULL );
6553 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6554 trw_layer_cancel_current_tp ( vtl, TRUE );
6556 if ( vtl->current_tpl == NULL )
6559 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6561 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6562 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6564 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6566 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6568 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6574 // Find available adjacent trackpoint
6575 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6577 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6578 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6580 // Delete current trackpoint
6581 vik_trackpoint_free ( vtl->current_tpl->data );
6582 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6584 // Set to current to the available adjacent trackpoint
6585 vtl->current_tpl = new_tpl;
6587 // Reset dialog with the available adjacent trackpoint
6588 if ( vtl->current_tp_track )
6589 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6591 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6595 // Delete current trackpoint
6596 vik_trackpoint_free ( vtl->current_tpl->data );
6597 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6598 trw_layer_cancel_current_tp ( vtl, FALSE );
6601 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6603 if ( vtl->current_tp_track )
6604 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6605 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
6607 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6609 if ( vtl->current_tp_track )
6610 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6611 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6613 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6615 trw_layer_insert_tp_after_current_tp ( vtl );
6616 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6618 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6619 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6622 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
6626 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6627 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
6628 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
6629 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
6630 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
6632 if ( vtl->current_tpl )
6633 if ( vtl->current_tp_track )
6634 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6635 /* set layer name and TP data */
6638 /***************************************************************************
6640 ***************************************************************************/
6642 /*** Utility data structures and functions ****/
6646 gint closest_x, closest_y;
6647 gpointer *closest_wp_id;
6648 VikWaypoint *closest_wp;
6654 gint closest_x, closest_y;
6655 gpointer closest_track_id;
6656 VikTrackpoint *closest_tp;
6661 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
6667 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
6669 // If waypoint has an image then use the image size to select
6671 gint slackx, slacky;
6672 slackx = wp->image_width / 2;
6673 slacky = wp->image_height / 2;
6675 if ( x <= params->x + slackx && x >= params->x - slackx
6676 && y <= params->y + slacky && y >= params->y - slacky ) {
6677 params->closest_wp_id = id;
6678 params->closest_wp = wp;
6679 params->closest_x = x;
6680 params->closest_y = y;
6683 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
6684 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
6685 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6687 params->closest_wp_id = id;
6688 params->closest_wp = wp;
6689 params->closest_x = x;
6690 params->closest_y = y;
6694 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
6696 GList *tpl = t->trackpoints;
6705 tp = VIK_TRACKPOINT(tpl->data);
6707 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
6709 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
6710 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
6711 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6713 params->closest_track_id = id;
6714 params->closest_tp = tp;
6715 params->closest_tpl = tpl;
6716 params->closest_x = x;
6717 params->closest_y = y;
6723 // ATM: Leave this as 'Track' only.
6724 // Not overly bothered about having a snap to route trackpoint capability
6725 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6727 TPSearchParams params;
6731 params.closest_track_id = NULL;
6732 params.closest_tp = NULL;
6733 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6734 return params.closest_tp;
6737 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6739 WPSearchParams params;
6743 params.closest_wp = NULL;
6744 params.closest_wp_id = NULL;
6745 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6746 return params.closest_wp;
6750 // Some forward declarations
6751 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
6752 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
6753 static void marker_end_move ( tool_ed_t *t );
6756 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6760 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6762 // Here always allow snapping back to the original location
6763 // this is useful when one decides not to move the thing afterall
6764 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
6767 if ( event->state & GDK_CONTROL_MASK )
6769 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6771 new_coord = tp->coord;
6775 if ( event->state & GDK_SHIFT_MASK )
6777 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6779 new_coord = wp->coord;
6783 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6785 marker_moveto ( t, x, y );
6792 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6794 if ( t->holding && event->button == 1 )
6797 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6800 if ( event->state & GDK_CONTROL_MASK )
6802 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6804 new_coord = tp->coord;
6808 if ( event->state & GDK_SHIFT_MASK )
6810 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6812 new_coord = wp->coord;
6815 marker_end_move ( t );
6817 // Determine if working on a waypoint or a trackpoint
6818 if ( t->is_waypoint )
6819 vtl->current_wp->coord = new_coord;
6821 if ( vtl->current_tpl ) {
6822 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6825 if ( vtl->current_tp_track )
6826 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6831 vtl->current_wp = NULL;
6832 vtl->current_wp_id = NULL;
6833 trw_layer_cancel_current_tp ( vtl, FALSE );
6835 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6842 Returns true if a waypoint or track is found near the requested event position for this particular layer
6843 The item found is automatically selected
6844 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
6846 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
6848 if ( event->button != 1 )
6851 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6854 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
6857 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
6859 if (vtl->waypoints_visible) {
6860 WPSearchParams wp_params;
6861 wp_params.vvp = vvp;
6862 wp_params.x = event->x;
6863 wp_params.y = event->y;
6864 wp_params.closest_wp_id = NULL;
6865 wp_params.closest_wp = NULL;
6867 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
6869 if ( wp_params.closest_wp ) {
6872 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
6874 // Too easy to move it so must be holding shift to start immediately moving it
6875 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
6876 if ( event->state & GDK_SHIFT_MASK ||
6877 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
6878 // Put into 'move buffer'
6879 // NB vvp & vw already set in tet
6880 tet->vtl = (gpointer)vtl;
6881 tet->is_waypoint = TRUE;
6883 marker_begin_move (tet, event->x, event->y);
6886 vtl->current_wp = wp_params.closest_wp;
6887 vtl->current_wp_id = wp_params.closest_wp_id;
6889 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6895 // Used for both track and route lists
6896 TPSearchParams tp_params;
6897 tp_params.vvp = vvp;
6898 tp_params.x = event->x;
6899 tp_params.y = event->y;
6900 tp_params.closest_track_id = NULL;
6901 tp_params.closest_tp = NULL;
6903 if (vtl->tracks_visible) {
6904 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
6906 if ( tp_params.closest_tp ) {
6908 // Always select + highlight the track
6909 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
6911 tet->is_waypoint = FALSE;
6913 // Select the Trackpoint
6914 // Can move it immediately when control held or it's the previously selected tp
6915 if ( event->state & GDK_CONTROL_MASK ||
6916 vtl->current_tpl == tp_params.closest_tpl ) {
6917 // Put into 'move buffer'
6918 // NB vvp & vw already set in tet
6919 tet->vtl = (gpointer)vtl;
6920 marker_begin_move (tet, event->x, event->y);
6923 vtl->current_tpl = tp_params.closest_tpl;
6924 vtl->current_tp_id = tp_params.closest_track_id;
6925 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
6927 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
6930 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6932 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6937 // Try again for routes
6938 if (vtl->routes_visible) {
6939 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
6941 if ( tp_params.closest_tp ) {
6943 // Always select + highlight the track
6944 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
6946 tet->is_waypoint = FALSE;
6948 // Select the Trackpoint
6949 // Can move it immediately when control held or it's the previously selected tp
6950 if ( event->state & GDK_CONTROL_MASK ||
6951 vtl->current_tpl == tp_params.closest_tpl ) {
6952 // Put into 'move buffer'
6953 // NB vvp & vw already set in tet
6954 tet->vtl = (gpointer)vtl;
6955 marker_begin_move (tet, event->x, event->y);
6958 vtl->current_tpl = tp_params.closest_tpl;
6959 vtl->current_tp_id = tp_params.closest_track_id;
6960 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
6962 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
6965 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6967 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6972 /* these aren't the droids you're looking for */
6973 vtl->current_wp = NULL;
6974 vtl->current_wp_id = NULL;
6975 trw_layer_cancel_current_tp ( vtl, FALSE );
6978 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
6983 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6985 if ( event->button != 3 )
6988 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6991 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
6994 /* Post menu for the currently selected item */
6996 /* See if a track is selected */
6997 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6998 if ( track && track->visible ) {
7000 if ( track->name ) {
7002 if ( vtl->track_right_click_menu )
7003 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
7005 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7012 if ( track->is_route )
7013 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7015 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7017 if ( trkf && udataU.uuid ) {
7020 if ( track->is_route )
7021 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7023 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7025 trw_layer_sublayer_add_menu_items ( vtl,
7026 vtl->track_right_click_menu,
7028 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7034 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7040 /* See if a waypoint is selected */
7041 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7042 if ( waypoint && waypoint->visible ) {
7043 if ( waypoint->name ) {
7045 if ( vtl->wp_right_click_menu )
7046 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
7048 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7051 udata.wp = waypoint;
7054 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7056 if ( wpf && udata.uuid ) {
7057 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7059 trw_layer_sublayer_add_menu_items ( vtl,
7060 vtl->wp_right_click_menu,
7062 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7067 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7076 /* background drawing hook, to be passed the viewport */
7077 static gboolean tool_sync_done = TRUE;
7079 static gboolean tool_sync(gpointer data)
7081 VikViewport *vvp = data;
7082 gdk_threads_enter();
7083 vik_viewport_sync(vvp);
7084 tool_sync_done = TRUE;
7085 gdk_threads_leave();
7089 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7092 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7093 gdk_gc_set_function ( t->gc, GDK_INVERT );
7094 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7095 vik_viewport_sync(t->vvp);
7100 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7102 VikViewport *vvp = t->vvp;
7103 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7104 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7108 if (tool_sync_done) {
7109 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7110 tool_sync_done = FALSE;
7114 static void marker_end_move ( tool_ed_t *t )
7116 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7117 g_object_unref ( t->gc );
7121 /*** Edit waypoint ****/
7123 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7125 tool_ed_t *t = g_new(tool_ed_t, 1);
7131 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7133 WPSearchParams params;
7134 tool_ed_t *t = data;
7135 VikViewport *vvp = t->vvp;
7137 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7144 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7147 if ( vtl->current_wp && vtl->current_wp->visible )
7149 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7151 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7153 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7154 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7156 if ( event->button == 3 )
7157 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7159 marker_begin_move(t, event->x, event->y);
7166 params.x = event->x;
7167 params.y = event->y;
7168 params.closest_wp_id = NULL;
7169 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7170 params.closest_wp = NULL;
7171 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7172 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7174 // how do we get here?
7175 marker_begin_move(t, event->x, event->y);
7176 g_critical("shouldn't be here");
7179 else if ( params.closest_wp )
7181 if ( event->button == 3 )
7182 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7184 vtl->waypoint_rightclick = FALSE;
7186 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7188 vtl->current_wp = params.closest_wp;
7189 vtl->current_wp_id = params.closest_wp_id;
7191 /* could make it so don't update if old WP is off screen and new is null but oh well */
7192 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7196 vtl->current_wp = NULL;
7197 vtl->current_wp_id = NULL;
7198 vtl->waypoint_rightclick = FALSE;
7199 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7203 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7205 tool_ed_t *t = data;
7206 VikViewport *vvp = t->vvp;
7208 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7213 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7216 if ( event->state & GDK_CONTROL_MASK )
7218 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7220 new_coord = tp->coord;
7224 if ( event->state & GDK_SHIFT_MASK )
7226 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7227 if ( wp && wp != vtl->current_wp )
7228 new_coord = wp->coord;
7233 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7235 marker_moveto ( t, x, y );
7242 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7244 tool_ed_t *t = data;
7245 VikViewport *vvp = t->vvp;
7247 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7250 if ( t->holding && event->button == 1 )
7253 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7256 if ( event->state & GDK_CONTROL_MASK )
7258 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7260 new_coord = tp->coord;
7264 if ( event->state & GDK_SHIFT_MASK )
7266 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7267 if ( wp && wp != vtl->current_wp )
7268 new_coord = wp->coord;
7271 marker_end_move ( t );
7273 vtl->current_wp->coord = new_coord;
7274 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7277 /* PUT IN RIGHT PLACE!!! */
7278 if ( event->button == 3 && vtl->waypoint_rightclick )
7280 if ( vtl->wp_right_click_menu )
7281 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7282 if ( vtl->current_wp ) {
7283 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7284 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 );
7285 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7287 vtl->waypoint_rightclick = FALSE;
7292 /*** New track ****/
7294 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7301 GdkDrawable *drawable;
7307 * Draw specified pixmap
7309 static gboolean draw_sync ( gpointer data )
7311 draw_sync_t *ds = (draw_sync_t*) data;
7312 // Sometimes don't want to draw
7313 // normally because another update has taken precedent such as panning the display
7314 // which means this pixmap is no longer valid
7315 if ( ds->vtl->draw_sync_do ) {
7316 gdk_threads_enter();
7317 gdk_draw_drawable (ds->drawable,
7320 0, 0, 0, 0, -1, -1);
7321 ds->vtl->draw_sync_done = TRUE;
7322 gdk_threads_leave();
7327 static gchar* distance_string (gdouble distance)
7331 /* draw label with distance */
7332 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7333 switch (dist_units) {
7334 case VIK_UNITS_DISTANCE_MILES:
7335 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7336 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7337 } else if (distance < 1609.4) {
7338 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7340 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7344 // VIK_UNITS_DISTANCE_KILOMETRES
7345 if (distance >= 1000 && distance < 100000) {
7346 g_sprintf(str, "%3.2f km", distance/1000.0);
7347 } else if (distance < 1000) {
7348 g_sprintf(str, "%d m", (int)distance);
7350 g_sprintf(str, "%d km", (int)distance/1000);
7354 return g_strdup (str);
7358 * Actually set the message in statusbar
7360 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
7362 // Only show elevation data when track has some elevation properties
7363 gchar str_gain_loss[64];
7364 str_gain_loss[0] = '\0';
7366 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7367 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7368 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7370 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7373 // Write with full gain/loss information
7374 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
7375 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
7380 * Figure out what information should be set in the statusbar and then write it
7382 static void update_statusbar ( VikTrwLayer *vtl )
7384 // Get elevation data
7385 gdouble elev_gain, elev_loss;
7386 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7388 /* Find out actual distance of current track */
7389 gdouble distance = vik_track_get_length (vtl->current_track);
7390 gchar *str = distance_string (distance);
7392 statusbar_write (str, elev_gain, elev_loss, vtl);
7398 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7400 /* if we haven't sync'ed yet, we don't have time to do more. */
7401 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7402 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7403 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7405 static GdkPixmap *pixmap = NULL;
7407 // Need to check in case window has been resized
7408 w1 = vik_viewport_get_width(vvp);
7409 h1 = vik_viewport_get_height(vvp);
7411 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7413 gdk_drawable_get_size (pixmap, &w2, &h2);
7414 if (w1 != w2 || h1 != h2) {
7415 g_object_unref ( G_OBJECT ( pixmap ) );
7416 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7419 // Reset to background
7420 gdk_draw_drawable (pixmap,
7421 vtl->current_track_newpoint_gc,
7422 vik_viewport_get_pixmap(vvp),
7423 0, 0, 0, 0, -1, -1);
7425 draw_sync_t *passalong;
7428 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7430 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7431 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7432 // thus when we come to reset to the background it would include what we have already drawn!!
7433 gdk_draw_line ( pixmap,
7434 vtl->current_track_newpoint_gc,
7435 x1, y1, event->x, event->y );
7436 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7438 /* Find out actual distance of current track */
7439 gdouble distance = vik_track_get_length (vtl->current_track);
7441 // Now add distance to where the pointer is //
7444 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7445 vik_coord_to_latlon ( &coord, &ll );
7446 distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
7448 // Get elevation data
7449 gdouble elev_gain, elev_loss;
7450 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7452 // Adjust elevation data (if available) for the current pointer position
7454 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7455 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7456 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7457 // Adjust elevation of last track point
7458 if ( elev_new > last_tpt->altitude )
7460 elev_gain += elev_new - last_tpt->altitude;
7463 elev_loss += last_tpt->altitude - elev_new;
7467 gchar *str = distance_string (distance);
7469 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7470 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
7472 pango_layout_set_text (pl, str, -1);
7474 pango_layout_get_pixel_size ( pl, &wd, &hd );
7477 // offset from cursor a bit depending on font size
7481 // Create a background block to make the text easier to read over the background map
7482 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7483 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7484 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7486 g_object_unref ( G_OBJECT ( pl ) );
7487 g_object_unref ( G_OBJECT ( background_block_gc ) );
7489 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7490 passalong->vtl = vtl;
7491 passalong->pixmap = pixmap;
7492 passalong->drawable = GTK_WIDGET(vvp)->window;
7493 passalong->gc = vtl->current_track_newpoint_gc;
7495 // Update statusbar with full gain/loss information
7496 statusbar_write (str, elev_gain, elev_loss, vtl);
7500 // draw pixmap when we have time to
7501 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7502 vtl->draw_sync_done = FALSE;
7503 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7505 return VIK_LAYER_TOOL_ACK;
7508 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7510 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7511 vtl->current_track = NULL;
7512 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7514 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7516 if ( vtl->current_track->trackpoints )
7518 GList *last = g_list_last(vtl->current_track->trackpoints);
7519 g_free ( last->data );
7520 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7523 update_statusbar ( vtl );
7525 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7532 * Common function to handle trackpoint button requests on either a route or a track
7533 * . enables adding a point via normal click
7534 * . enables removal of last point via right click
7535 * . finishing of the track or route via double clicking
7537 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7541 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7544 if ( event->button == 2 ) {
7545 // As the display is panning, the new track pixmap is now invalid so don't draw it
7546 // otherwise this drawing done results in flickering back to an old image
7547 vtl->draw_sync_do = FALSE;
7551 if ( event->button == 3 )
7553 if ( !vtl->current_track )
7556 if ( vtl->current_track->trackpoints )
7558 GList *last = g_list_last(vtl->current_track->trackpoints);
7559 g_free ( last->data );
7560 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7562 update_statusbar ( vtl );
7564 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7568 if ( event->type == GDK_2BUTTON_PRESS )
7570 /* subtract last (duplicate from double click) tp then end */
7571 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7573 GList *last = g_list_last(vtl->current_track->trackpoints);
7574 g_free ( last->data );
7575 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7576 /* undo last, then end */
7577 vtl->current_track = NULL;
7579 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7583 tp = vik_trackpoint_new();
7584 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
7586 /* snap to other TP */
7587 if ( event->state & GDK_CONTROL_MASK )
7589 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7591 tp->coord = other_tp->coord;
7594 tp->newsegment = FALSE;
7595 tp->has_timestamp = FALSE;
7598 if ( vtl->current_track ) {
7599 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
7600 /* Auto attempt to get elevation from DEM data (if it's available) */
7601 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
7604 vtl->ct_x1 = vtl->ct_x2;
7605 vtl->ct_y1 = vtl->ct_y2;
7606 vtl->ct_x2 = event->x;
7607 vtl->ct_y2 = event->y;
7609 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7613 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7615 // ----------------------------------------------------- if current is a route - switch to new track
7616 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
7618 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
7619 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
7621 vtl->current_track = vik_track_new();
7622 vtl->current_track->visible = TRUE;
7623 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
7628 return tool_new_track_or_route_click ( vtl, event, vvp );
7631 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7633 if ( event->button == 2 ) {
7634 // Pan moving ended - enable potential point drawing again
7635 vtl->draw_sync_do = TRUE;
7636 vtl->draw_sync_done = TRUE;
7640 /*** New route ****/
7642 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
7647 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7649 // -------------------------- if current is a track - switch to new route
7650 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
7652 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
7653 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
7655 vtl->current_track = vik_track_new();
7656 vtl->current_track->visible = TRUE;
7657 vtl->current_track->is_route = TRUE;
7658 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
7663 return tool_new_track_or_route_click ( vtl, event, vvp );
7666 /*** New waypoint ****/
7668 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7673 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7676 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7678 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
7679 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
7680 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7685 /*** Edit trackpoint ****/
7687 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
7689 tool_ed_t *t = g_new(tool_ed_t, 1);
7695 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7697 tool_ed_t *t = data;
7698 VikViewport *vvp = t->vvp;
7699 TPSearchParams params;
7700 /* OUTDATED DOCUMENTATION:
7701 find 5 pixel range on each side. then put these UTM, and a pointer
7702 to the winning track name (and maybe the winning track itself), and a
7703 pointer to the winning trackpoint, inside an array or struct. pass
7704 this along, do a foreach on the tracks which will do a foreach on the
7707 params.x = event->x;
7708 params.y = event->y;
7709 params.closest_track_id = NULL;
7710 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7711 params.closest_tp = NULL;
7713 if ( event->button != 1 )
7716 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7719 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
7722 if ( vtl->current_tpl )
7724 /* 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.) */
7725 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7726 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
7731 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
7733 if ( current_tr->visible &&
7734 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7735 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
7736 marker_begin_move ( t, event->x, event->y );
7742 if ( vtl->tracks_visible )
7743 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7745 if ( params.closest_tp )
7747 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
7748 vtl->current_tpl = params.closest_tpl;
7749 vtl->current_tp_id = params.closest_track_id;
7750 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
7751 trw_layer_tpwin_init ( vtl );
7752 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7753 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7757 if ( vtl->routes_visible )
7758 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
7760 if ( params.closest_tp )
7762 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
7763 vtl->current_tpl = params.closest_tpl;
7764 vtl->current_tp_id = params.closest_track_id;
7765 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
7766 trw_layer_tpwin_init ( vtl );
7767 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7768 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7772 /* these aren't the droids you're looking for */
7776 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7778 tool_ed_t *t = data;
7779 VikViewport *vvp = t->vvp;
7781 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7787 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7790 if ( event->state & GDK_CONTROL_MASK )
7792 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7793 if ( tp && tp != vtl->current_tpl->data )
7794 new_coord = tp->coord;
7796 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7799 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7800 marker_moveto ( t, x, y );
7808 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7810 tool_ed_t *t = data;
7811 VikViewport *vvp = t->vvp;
7813 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7815 if ( event->button != 1)
7820 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7823 if ( event->state & GDK_CONTROL_MASK )
7825 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7826 if ( tp && tp != vtl->current_tpl->data )
7827 new_coord = tp->coord;
7830 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7832 marker_end_move ( t );
7834 /* diff dist is diff from orig */
7836 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7838 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7845 #ifdef VIK_CONFIG_GOOGLE
7846 /*** Route Finder ***/
7847 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
7852 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7855 if ( !vtl ) return FALSE;
7856 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
7857 if ( event->button == 3 && vtl->route_finder_current_track ) {
7859 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
7861 vtl->route_finder_coord = *new_end;
7863 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7864 /* remove last ' to:...' */
7865 if ( vtl->route_finder_current_track->comment ) {
7866 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
7867 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
7868 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
7869 last_to - vtl->route_finder_current_track->comment - 1);
7870 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
7875 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
7876 struct LatLon start, end;
7877 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
7878 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
7881 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
7882 vik_coord_to_latlon ( &(tmp), &end );
7883 vtl->route_finder_coord = tmp; /* for continuations */
7885 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
7886 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
7887 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
7889 vtl->route_finder_check_added_track = TRUE;
7890 vtl->route_finder_started = FALSE;
7893 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
7894 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
7895 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
7896 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
7897 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
7898 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
7901 /* see if anything was done -- a track was added or appended to */
7902 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
7903 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 ) );
7904 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
7905 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
7906 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
7907 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
7909 vtl->route_finder_added_track = NULL;
7910 vtl->route_finder_check_added_track = FALSE;
7911 vtl->route_finder_append = FALSE;
7913 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7915 vtl->route_finder_started = TRUE;
7916 vtl->route_finder_coord = tmp;
7917 vtl->route_finder_current_track = NULL;
7923 /*** Show picture ****/
7925 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
7930 /* Params are: vvp, event, last match found or NULL */
7931 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
7933 if ( wp->image && wp->visible )
7935 gint x, y, slackx, slacky;
7936 GdkEventButton *event = (GdkEventButton *) params[1];
7938 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
7939 slackx = wp->image_width / 2;
7940 slacky = wp->image_height / 2;
7941 if ( x <= event->x + slackx && x >= event->x - slackx
7942 && y <= event->y + slacky && y >= event->y - slacky )
7944 params[2] = wp->image; /* we've found a match. however continue searching
7945 * since we want to find the last match -- that
7946 * is, the match that was drawn last. */
7951 static void trw_layer_show_picture ( gpointer pass_along[6] )
7953 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
7955 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
7958 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
7959 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
7960 g_free ( quoted_file );
7961 if ( ! g_spawn_command_line_async ( cmd, &err ) )
7963 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() );
7964 g_error_free ( err );
7967 #endif /* WINDOWS */
7970 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7972 gpointer params[3] = { vvp, event, NULL };
7973 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7975 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
7978 static gpointer pass_along[6];
7979 pass_along[0] = vtl;
7980 pass_along[5] = params[2];
7981 trw_layer_show_picture ( pass_along );
7982 return TRUE; /* found a match */
7985 return FALSE; /* go through other layers, searching for a match */
7988 /***************************************************************************
7990 ***************************************************************************/
7996 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
7998 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
7999 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8002 /* Structure for thumbnail creating data used in the background thread */
8004 VikTrwLayer *vtl; // Layer needed for redrawing
8005 GSList *pics; // Image list
8006 } thumbnail_create_thread_data;
8008 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8010 guint total = g_slist_length(tctd->pics), done = 0;
8011 while ( tctd->pics )
8013 a_thumbnails_create ( (gchar *) tctd->pics->data );
8014 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8016 return -1; /* Abort thread */
8018 tctd->pics = tctd->pics->next;
8021 // Redraw to show the thumbnails as they are now created
8022 if ( IS_VIK_LAYER(tctd->vtl) )
8023 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
8028 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8030 while ( tctd->pics )
8032 g_free ( tctd->pics->data );
8033 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8038 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8040 if ( ! vtl->has_verified_thumbnails )
8042 GSList *pics = NULL;
8043 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8046 gint len = g_slist_length ( pics );
8047 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8048 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8051 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8053 (vik_thr_func) create_thumbnails_thread,
8055 (vik_thr_free_func) thumbnail_create_thread_free,
8063 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8065 return vtl->coord_mode;
8069 * Uniquify the whole layer
8070 * Also requires the layers panel as the names shown there need updating too
8071 * Returns whether the operation was successful or not
8073 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8076 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8077 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8078 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8084 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8086 vik_coord_convert ( &(wp->coord), *dest_mode );
8089 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
8091 vik_track_convert ( tr, *dest_mode );
8094 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8096 if ( vtl->coord_mode != dest_mode )
8098 vtl->coord_mode = dest_mode;
8099 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8100 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8104 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8106 vtl->menu_selection = selection;
8109 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8111 return (vtl->menu_selection);
8114 /* ----------- Downloading maps along tracks --------------- */
8116 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8118 /* TODO: calculating based on current size of viewport */
8119 const gdouble w_at_zoom_0_125 = 0.0013;
8120 const gdouble h_at_zoom_0_125 = 0.0011;
8121 gdouble zoom_factor = zoom_level/0.125;
8123 wh->lat = h_at_zoom_0_125 * zoom_factor;
8124 wh->lon = w_at_zoom_0_125 * zoom_factor;
8126 return 0; /* all OK */
8129 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8131 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8132 (dist->lat >= ABS(to->north_south - from->north_south)))
8135 VikCoord *coord = g_malloc(sizeof(VikCoord));
8136 coord->mode = VIK_COORD_LATLON;
8138 if (ABS(gradient) < 1) {
8139 if (from->east_west > to->east_west)
8140 coord->east_west = from->east_west - dist->lon;
8142 coord->east_west = from->east_west + dist->lon;
8143 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8145 if (from->north_south > to->north_south)
8146 coord->north_south = from->north_south - dist->lat;
8148 coord->north_south = from->north_south + dist->lat;
8149 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8155 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8157 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8158 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8160 VikCoord *next = from;
8162 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8164 list = g_list_prepend(list, next);
8170 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8172 typedef struct _Rect {
8177 #define GLRECT(iter) ((Rect *)((iter)->data))
8180 GList *rects_to_download = NULL;
8183 if (get_download_area_width(vvp, zoom_level, &wh))
8186 GList *iter = tr->trackpoints;
8190 gboolean new_map = TRUE;
8191 VikCoord *cur_coord, tl, br;
8194 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8196 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8197 rect = g_malloc(sizeof(Rect));
8200 rect->center = *cur_coord;
8201 rects_to_download = g_list_prepend(rects_to_download, rect);
8206 gboolean found = FALSE;
8207 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8208 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8219 GList *fillins = NULL;
8220 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8221 /* seems that ATM the function get_next_coord works only for LATLON */
8222 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8223 /* fill-ins for far apart points */
8224 GList *cur_rect, *next_rect;
8225 for (cur_rect = rects_to_download;
8226 (next_rect = cur_rect->next) != NULL;
8227 cur_rect = cur_rect->next) {
8228 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8229 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8230 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8234 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8237 GList *iter = fillins;
8239 cur_coord = (VikCoord *)(iter->data);
8240 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8241 rect = g_malloc(sizeof(Rect));
8244 rect->center = *cur_coord;
8245 rects_to_download = g_list_prepend(rects_to_download, rect);
8250 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8251 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8255 for (iter = fillins; iter; iter = iter->next)
8257 g_list_free(fillins);
8259 if (rects_to_download) {
8260 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8261 g_free(rect_iter->data);
8262 g_list_free(rects_to_download);
8266 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8269 gint selected_map, default_map;
8270 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8271 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8272 gint selected_zoom, default_zoom;
8276 VikTrwLayer *vtl = pass_along[0];
8277 VikLayersPanel *vlp = pass_along[1];
8279 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8280 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8282 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8286 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8288 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8289 int num_maps = g_list_length(vmls);
8292 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8296 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8297 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8299 gchar **np = map_names;
8300 VikMapsLayer **lp = map_layers;
8301 for (i = 0; i < num_maps; i++) {
8302 gboolean dup = FALSE;
8303 vml = (VikMapsLayer *)(vmls->data);
8304 for (j = 0; j < i; j++) { /* no duplicate allowed */
8305 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8312 *np++ = vik_maps_layer_get_map_label(vml);
8318 num_maps = lp - map_layers;
8320 for (default_map = 0; default_map < num_maps; default_map++) {
8321 /* TODO: check for parent layer's visibility */
8322 if (VIK_LAYER(map_layers[default_map])->visible)
8325 default_map = (default_map == num_maps) ? 0 : default_map;
8327 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8328 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8329 if (cur_zoom == zoom_vals[default_zoom])
8332 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8334 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8337 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8340 for (i = 0; i < num_maps; i++)
8341 g_free(map_names[i]);
8349 /**** lowest waypoint number calculation ***/
8350 static gint highest_wp_number_name_to_number(const gchar *name) {
8351 if ( strlen(name) == 3 ) {
8353 if ( n < 100 && name[0] != '0' )
8355 if ( n < 10 && name[0] != '0' )
8363 static void highest_wp_number_reset(VikTrwLayer *vtl)
8365 vtl->highest_wp_number = -1;
8368 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8370 /* if is bigger that top, add it */
8371 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8372 if ( new_wp_num > vtl->highest_wp_number )
8373 vtl->highest_wp_number = new_wp_num;
8376 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8378 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8379 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8380 if ( vtl->highest_wp_number == old_wp_num ) {
8382 vtl->highest_wp_number--;
8384 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8385 /* search down until we find something that *does* exist */
8387 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8388 vtl->highest_wp_number--;
8389 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8394 /* get lowest unused number */
8395 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8398 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8400 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8401 return g_strdup(buf);