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 6
81 #define VIK_TRW_LAYER_TRACK_GCS 10
82 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
83 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
84 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
85 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
86 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
87 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
89 #define DRAWMODE_BY_TRACK 0
90 #define DRAWMODE_BY_SPEED 1
91 #define DRAWMODE_ALL_SAME_COLOR 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 *track_1color_gc;
152 GdkColor track_color;
153 GdkGC *current_track_gc;
154 // Separate GC for a track's potential new point as drawn via separate method
155 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
156 GdkGC *current_track_newpoint_gc;
159 GdkGC *waypoint_text_gc;
160 GdkGC *waypoint_bg_gc;
161 GdkFont *waypoint_font;
162 VikTrack *current_track; // ATM shared between new tracks and new routes
163 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
164 gboolean draw_sync_done;
165 gboolean draw_sync_do;
167 VikCoordMode coord_mode;
169 /* wp editing tool */
170 VikWaypoint *current_wp;
171 gpointer current_wp_id;
173 gboolean waypoint_rightclick;
175 /* track editing tool */
177 VikTrack *current_tp_track;
178 gpointer current_tp_id;
179 VikTrwLayerTpwin *tpwin;
181 /* track editing tool -- more specifically, moving tps */
184 /* route finder tool */
185 gboolean route_finder_started;
186 VikCoord route_finder_coord;
187 gboolean route_finder_check_added_track;
188 VikTrack *route_finder_added_track;
189 VikTrack *route_finder_current_track;
190 gboolean route_finder_append;
197 guint16 image_cache_size;
199 /* for waypoint text */
200 PangoLayout *wplabellayout;
202 gboolean has_verified_thumbnails;
204 GtkMenu *wp_right_click_menu;
205 GtkMenu *track_right_click_menu;
208 VikStdLayerMenuItem menu_selection;
210 gint highest_wp_number;
213 /* A caached waypoint image. */
216 gchar *image; /* filename */
219 struct DrawingParams {
224 guint16 width, height;
225 gdouble cc; // Cosine factor in track directions
226 gdouble ss; // Sine factor in track directions
227 const VikCoord *center;
228 gboolean one_zone, lat_lon;
229 gdouble ce1, ce2, cn1, cn2;
232 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
234 static void trw_layer_delete_item ( gpointer pass_along[6] );
235 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
236 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
238 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] );
239 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
240 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
242 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
243 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
245 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
246 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
248 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
249 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
250 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
251 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
252 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
253 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
254 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
255 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
256 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
257 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
258 static void trw_layer_append_track ( gpointer pass_along[6] );
259 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
260 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
261 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
262 static void trw_layer_split_segments ( gpointer pass_along[6] );
263 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
264 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
265 static void trw_layer_reverse ( gpointer pass_along[6] );
266 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
267 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
268 static void trw_layer_show_picture ( gpointer pass_along[6] );
269 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
271 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
272 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
273 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
274 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
275 static void trw_layer_new_wp ( gpointer lav[2] );
276 static void trw_layer_new_track ( gpointer lav[2] );
277 static void trw_layer_new_route ( gpointer lav[2] );
278 static void trw_layer_finish_track ( gpointer lav[2] );
279 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
280 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
281 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
282 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
283 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
284 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
285 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
286 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
287 #ifdef VIK_CONFIG_GEOTAG
288 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
289 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
290 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
291 static void trw_layer_geotagging ( gpointer lav[2] );
293 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
294 #ifdef VIK_CONFIG_GOOGLE
295 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
297 #ifdef VIK_CONFIG_OPENSTREETMAP
298 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
299 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
301 #ifdef VIK_CONFIG_GEOCACHES
302 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
304 #ifdef VIK_CONFIG_GEOTAG
305 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
307 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
308 static void trw_layer_gps_upload ( gpointer lav[2] );
310 // Specific route versions:
311 // Most track handling functions can handle operating on the route list
312 // However these ones are easier in separate functions
313 static void trw_layer_auto_routes_view ( gpointer lav[2] );
314 static void trw_layer_delete_all_routes ( gpointer lav[2] );
315 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
318 static void trw_layer_properties_item ( gpointer pass_along[7] );
319 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
320 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
321 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
323 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
324 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
325 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
327 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
328 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
329 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
330 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
332 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
333 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
334 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
335 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
336 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
337 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
338 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
339 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
340 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
341 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
342 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
343 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
344 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
345 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
346 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
347 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
348 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
349 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
350 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
351 #ifdef VIK_CONFIG_GOOGLE
352 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
353 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
356 static void cached_pixbuf_free ( CachedPixbuf *cp );
357 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
359 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
360 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
362 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
363 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
365 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
366 static void highest_wp_number_reset(VikTrwLayer *vtl);
367 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
368 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
370 // Note for the following tool GtkRadioActionEntry texts:
371 // the very first text value is an internal name not displayed anywhere
372 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
373 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
374 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
375 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
376 static VikToolInterface trw_layer_tools[] = {
377 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
378 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
379 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
381 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
383 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
384 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
385 (VikToolMouseFunc) tool_new_track_click,
386 (VikToolMouseMoveFunc) tool_new_track_move,
387 (VikToolMouseFunc) tool_new_track_release,
388 (VikToolKeyFunc) tool_new_track_key_press,
389 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
390 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
392 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
393 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
394 (VikToolMouseFunc) tool_new_route_click,
395 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
396 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
397 (VikToolKeyFunc) tool_new_track_key_press, // -/#
398 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
399 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
401 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
402 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
403 (VikToolMouseFunc) tool_edit_waypoint_click,
404 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
405 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
407 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
409 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
410 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
411 (VikToolMouseFunc) tool_edit_trackpoint_click,
412 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
413 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
415 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
417 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
418 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
419 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
421 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
423 #ifdef VIK_CONFIG_GOOGLE
424 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
425 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
426 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
428 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
433 TOOL_CREATE_WAYPOINT=0,
437 TOOL_EDIT_TRACKPOINT,
439 #ifdef VIK_CONFIG_GOOGLE
445 /****** PARAMETERS ******/
447 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
448 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
450 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
451 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
453 #define MIN_POINT_SIZE 2
454 #define MAX_POINT_SIZE 10
456 #define MIN_ARROW_SIZE 3
457 #define MAX_ARROW_SIZE 20
459 static VikLayerParamScale params_scales[] = {
460 /* min max step digits */
461 { 1, 10, 1, 0 }, /* line_thickness */
462 { 0, 100, 1, 0 }, /* track draw speed factor */
463 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
464 /* 5 * step == how much to turn */
465 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
466 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
467 { 5, 500, 5, 0 }, // 5: image cache_size - " "
468 { 0, 8, 1, 0 }, // 6: Background line thickness
469 { 1, 64, 1, 0 }, /* wpsize */
470 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
471 { 1, 100, 1, 0 }, // 9: elevation factor
472 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
473 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
476 static gchar* params_font_sizes[] = {
477 N_("Extra Extra Small"),
483 N_("Extra Extra Large"),
486 VikLayerParam trw_layer_params[] = {
487 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
488 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
489 { "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
491 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL },
492 { "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
493 N_("The color used when 'All Tracks Same Color' drawing mode is selected") },
494 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
495 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[0], NULL, NULL },
496 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
497 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL },
498 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
499 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL },
500 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
501 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[9], NULL, NULL },
503 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
504 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time") },
505 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[8], NULL, NULL },
507 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[6], NULL, NULL},
508 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
509 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
510 N_("The percentage factor away from the average speed determining the color used") },
512 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
513 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL },
514 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
515 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
516 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
517 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
518 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL },
519 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[7], NULL, NULL },
520 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
522 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
523 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[3], NULL, NULL },
524 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[4], NULL, NULL },
525 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[5], NULL, NULL },
528 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
530 // Sublayer visibilities
569 *** 1) Add to trw_layer_params and enumeration
570 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
573 /****** END PARAMETERS ******/
575 static VikTrwLayer* trw_layer_new ( gint drawmode );
576 /* Layer Interface function definitions */
577 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
578 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
579 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp );
580 static void trw_layer_free ( VikTrwLayer *trwlayer );
581 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
582 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
583 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
584 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
585 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
586 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
587 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
588 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
589 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
590 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
591 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
592 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
593 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
594 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
595 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
596 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
597 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
598 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
599 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
600 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
601 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
602 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
603 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
604 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
605 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
606 /* End Layer Interface function definitions */
608 VikLayerInterface vik_trw_layer_interface = {
615 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
619 params_groups, /* params_groups */
620 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
624 (VikLayerFuncCreate) trw_layer_create,
625 (VikLayerFuncRealize) trw_layer_realize,
626 (VikLayerFuncPostRead) trw_layer_post_read,
627 (VikLayerFuncFree) trw_layer_free,
629 (VikLayerFuncProperties) NULL,
630 (VikLayerFuncDraw) trw_layer_draw,
631 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
633 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
634 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
636 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
637 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
639 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
640 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
641 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
642 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
643 (VikLayerFuncLayerSelected) trw_layer_selected,
645 (VikLayerFuncMarshall) trw_layer_marshall,
646 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
648 (VikLayerFuncSetParam) trw_layer_set_param,
649 (VikLayerFuncGetParam) trw_layer_get_param,
651 (VikLayerFuncReadFileData) a_gpspoint_read_file,
652 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
654 (VikLayerFuncDeleteItem) trw_layer_del_item,
655 (VikLayerFuncCutItem) trw_layer_cut_item,
656 (VikLayerFuncCopyItem) trw_layer_copy_item,
657 (VikLayerFuncPasteItem) trw_layer_paste_item,
658 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
660 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
662 (VikLayerFuncSelectClick) trw_layer_select_click,
663 (VikLayerFuncSelectMove) trw_layer_select_move,
664 (VikLayerFuncSelectRelease) trw_layer_select_release,
665 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
668 GType vik_trw_layer_get_type ()
670 static GType vtl_type = 0;
674 static const GTypeInfo vtl_info =
676 sizeof (VikTrwLayerClass),
677 NULL, /* base_init */
678 NULL, /* base_finalize */
679 NULL, /* class init */
680 NULL, /* class_finalize */
681 NULL, /* class_data */
682 sizeof (VikTrwLayer),
684 NULL /* instance init */
686 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
692 static void trw_layer_del_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 (1); // Confirm delete request
704 pass_along[5] = NULL;
706 trw_layer_delete_item ( pass_along );
709 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
711 static gpointer pass_along[6];
717 pass_along[1] = NULL;
718 pass_along[2] = GINT_TO_POINTER (subtype);
719 pass_along[3] = sublayer;
720 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
721 pass_along[5] = NULL;
723 trw_layer_copy_item_cb(pass_along);
724 trw_layer_cut_item_cb(pass_along);
727 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
729 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
730 gint subtype = GPOINTER_TO_INT (pass_along[2]);
731 gpointer * sublayer = pass_along[3];
735 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
739 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
740 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
741 if ( wp && wp->name )
744 name = NULL; // Broken :(
746 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
747 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
748 if ( trk && trk->name )
751 name = NULL; // Broken :(
754 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
755 if ( trk && trk->name )
758 name = NULL; // Broken :(
761 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
762 subtype, len, name, data);
766 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
768 trw_layer_copy_item_cb(pass_along);
769 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
770 trw_layer_delete_item(pass_along);
773 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
775 // Slightly cheating method, routing via the panels capability
776 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
779 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
789 GByteArray *ba = g_byte_array_new ();
791 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
792 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
793 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
794 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
796 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
799 g_byte_array_append ( ba, id, il );
807 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
814 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
818 w = vik_waypoint_unmarshall ( item, len );
819 // When copying - we'll create a new name based on the original
820 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
821 vik_trw_layer_add_waypoint ( vtl, name, w );
822 waypoint_convert (NULL, w, &vtl->coord_mode);
824 // Consider if redraw necessary for the new item
825 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
826 vik_layer_emit_update ( VIK_LAYER(vtl) );
829 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
833 t = vik_track_unmarshall ( item, len );
834 // When copying - we'll create a new name based on the original
835 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
836 vik_trw_layer_add_track ( vtl, name, t );
837 vik_track_convert (t, vtl->coord_mode);
839 // Consider if redraw necessary for the new item
840 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
841 vik_layer_emit_update ( VIK_LAYER(vtl) );
844 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
848 t = vik_track_unmarshall ( item, len );
849 // When copying - we'll create a new name based on the original
850 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
851 vik_trw_layer_add_route ( vtl, name, t );
852 vik_track_convert (t, vtl->coord_mode);
854 // Consider if redraw necessary for the new item
855 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
856 vik_layer_emit_update ( VIK_LAYER(vtl) );
862 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
869 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
873 case PARAM_TV: vtl->tracks_visible = data.b; break;
874 case PARAM_WV: vtl->waypoints_visible = data.b; break;
875 case PARAM_RV: vtl->routes_visible = data.b; break;
876 case PARAM_DM: vtl->drawmode = data.u; break;
878 vtl->track_color = data.c;
879 trw_layer_new_track_gcs ( vtl, vp );
881 case PARAM_DP: vtl->drawpoints = data.b; break;
883 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
884 vtl->drawpoints_size = data.u;
886 case PARAM_DE: vtl->drawelevation = data.b; break;
887 case PARAM_DS: vtl->drawstops = data.b; break;
888 case PARAM_DL: vtl->drawlines = data.b; break;
889 case PARAM_DD: vtl->drawdirections = data.b; break;
891 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
892 vtl->drawdirections_size = data.u;
894 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
895 vtl->stop_length = data.u;
897 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
898 vtl->elevation_factor = data.u;
900 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
902 vtl->line_thickness = data.u;
903 trw_layer_new_track_gcs ( vtl, vp );
906 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
908 vtl->bg_line_thickness = data.u;
909 trw_layer_new_track_gcs ( vtl, vp );
912 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
913 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
914 case PARAM_DLA: vtl->drawlabels = data.b; break;
915 case PARAM_DI: vtl->drawimages = data.b; break;
916 case PARAM_IS: if ( data.u != vtl->image_size )
918 vtl->image_size = data.u;
919 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
920 g_queue_free ( vtl->image_cache );
921 vtl->image_cache = g_queue_new ();
924 case PARAM_IA: vtl->image_alpha = data.u; break;
925 case PARAM_ICS: vtl->image_cache_size = data.u;
926 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
927 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
929 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
930 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
931 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
932 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
933 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
934 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
935 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
936 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
941 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
943 VikLayerParamData rv;
946 case PARAM_TV: rv.b = vtl->tracks_visible; break;
947 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
948 case PARAM_RV: rv.b = vtl->routes_visible; break;
949 case PARAM_DM: rv.u = vtl->drawmode; break;
950 case PARAM_TC: rv.c = vtl->track_color; break;
951 case PARAM_DP: rv.b = vtl->drawpoints; break;
952 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
953 case PARAM_DE: rv.b = vtl->drawelevation; break;
954 case PARAM_EF: rv.u = vtl->elevation_factor; break;
955 case PARAM_DS: rv.b = vtl->drawstops; break;
956 case PARAM_SL: rv.u = vtl->stop_length; break;
957 case PARAM_DL: rv.b = vtl->drawlines; break;
958 case PARAM_DD: rv.b = vtl->drawdirections; break;
959 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
960 case PARAM_LT: rv.u = vtl->line_thickness; break;
961 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
962 case PARAM_DLA: rv.b = vtl->drawlabels; break;
963 case PARAM_DI: rv.b = vtl->drawimages; break;
964 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
965 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
966 case PARAM_IS: rv.u = vtl->image_size; break;
967 case PARAM_IA: rv.u = vtl->image_alpha; break;
968 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
969 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
970 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
971 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
972 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
973 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
974 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
975 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
976 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
981 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
988 // Use byte arrays to store sublayer data
989 // much like done elsewhere e.g. vik_layer_marshall_params()
990 GByteArray *ba = g_byte_array_new ( );
998 // the length of the item
999 // the sublayer type of item
1000 // the the actual item
1001 #define tlm_append(object_pointer, size, type) \
1003 object_length = (size); \
1004 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1005 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1006 g_byte_array_append ( ba, (object_pointer), object_length );
1008 // Layer parameters first
1009 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1010 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1011 g_byte_array_append ( ba, pd, pl );
1014 // Now sublayer data
1015 GHashTableIter iter;
1016 gpointer key, value;
1019 g_hash_table_iter_init ( &iter, vtl->waypoints );
1020 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1021 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1022 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1027 g_hash_table_iter_init ( &iter, vtl->tracks );
1028 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1029 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1030 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1035 g_hash_table_iter_init ( &iter, vtl->routes );
1036 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1037 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1038 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1048 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1050 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1052 gint consumed_length;
1054 // First the overall layer parameters
1055 memcpy(&pl, data, sizeof(pl));
1057 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1060 consumed_length = pl;
1061 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1063 #define tlm_size (*(gint *)data)
1064 // See marshalling above for order of how this is written
1066 data += sizeof_len_and_subtype + tlm_size;
1068 // Now the individual sublayers:
1070 while ( *data && consumed_length < len ) {
1071 // Normally four extra bytes at the end of the datastream
1072 // (since it's a GByteArray and that's where it's length is stored)
1073 // So only attempt read when there's an actual block of sublayer data
1074 if ( consumed_length + tlm_size < len ) {
1076 // Reuse pl to read the subtype from the data stream
1077 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1079 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1080 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1081 gchar *name = g_strdup ( trk->name );
1082 vik_trw_layer_add_track ( vtl, name, trk );
1085 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1086 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1087 gchar *name = g_strdup ( wp->name );
1088 vik_trw_layer_add_waypoint ( vtl, name, wp );
1091 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1092 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1093 gchar *name = g_strdup ( trk->name );
1094 vik_trw_layer_add_route ( vtl, name, trk );
1098 consumed_length += tlm_size + sizeof_len_and_subtype;
1101 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1106 // Keep interesting hash function at least visible
1108 static guint strcase_hash(gconstpointer v)
1110 // 31 bit hash function
1113 gchar s[128]; // malloc is too slow for reading big files
1116 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1117 p[i] = toupper(t[i]);
1123 for (p += 1; *p != '\0'; p++)
1124 h = (h << 5) - h + *p;
1131 static VikTrwLayer* trw_layer_new ( gint drawmode )
1133 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1134 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1136 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1137 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1139 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1140 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1141 // and with normal PC processing capabilities - it has negligibile performance impact
1142 // This also minimized the amount of rework - as the management of the hash tables already exists.
1144 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1145 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1146 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1148 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1149 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1150 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1151 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1152 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1153 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1156 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1157 rv->drawmode = drawmode;
1158 rv->drawpoints = TRUE;
1159 rv->drawpoints_size = MIN_POINT_SIZE;
1160 rv->drawdirections_size = 5;
1161 rv->elevation_factor = 30;
1162 rv->stop_length = 60;
1163 rv->drawlines = TRUE;
1164 rv->track_draw_speed_factor = 30.0;
1165 rv->line_thickness = 1;
1167 rv->draw_sync_done = TRUE;
1168 rv->draw_sync_do = TRUE;
1170 rv->image_cache = g_queue_new();
1171 rv->image_size = 64;
1172 rv->image_alpha = 255;
1173 rv->image_cache_size = 300;
1174 rv->drawimages = TRUE;
1175 rv->drawlabels = TRUE;
1176 // Everything else is 0, FALSE or NULL
1182 static void trw_layer_free ( VikTrwLayer *trwlayer )
1184 g_hash_table_destroy(trwlayer->waypoints);
1185 g_hash_table_destroy(trwlayer->tracks);
1187 /* ODC: replace with GArray */
1188 trw_layer_free_track_gcs ( trwlayer );
1190 if ( trwlayer->wp_right_click_menu )
1191 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1193 if ( trwlayer->track_right_click_menu )
1194 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1196 if ( trwlayer->wplabellayout != NULL)
1197 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1199 if ( trwlayer->waypoint_gc != NULL )
1200 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1202 if ( trwlayer->waypoint_text_gc != NULL )
1203 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1205 if ( trwlayer->waypoint_bg_gc != NULL )
1206 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1208 if ( trwlayer->tpwin != NULL )
1209 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1211 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1212 g_queue_free ( trwlayer->image_cache );
1215 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1219 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1220 dp->xmpp = vik_viewport_get_xmpp ( vp );
1221 dp->ympp = vik_viewport_get_ympp ( vp );
1222 dp->width = vik_viewport_get_width ( vp );
1223 dp->height = vik_viewport_get_height ( vp );
1224 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1225 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1227 dp->center = vik_viewport_get_center ( vp );
1228 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1229 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1234 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1235 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1236 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1238 dp->ce1 = dp->center->east_west-w2;
1239 dp->ce2 = dp->center->east_west+w2;
1240 dp->cn1 = dp->center->north_south-h2;
1241 dp->cn2 = dp->center->north_south+h2;
1242 } else if ( dp->lat_lon ) {
1243 VikCoord upperleft, bottomright;
1244 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1245 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1246 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1247 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1248 dp->ce1 = upperleft.east_west;
1249 dp->ce2 = bottomright.east_west;
1250 dp->cn1 = bottomright.north_south;
1251 dp->cn2 = upperleft.north_south;
1256 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1257 * Here a simple traffic like light colour system is used:
1258 * . slow points are red
1259 * . average is yellow
1260 * . fast points are green
1262 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1265 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1266 if ( average_speed > 0 ) {
1267 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1268 if ( rv < low_speed )
1269 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1270 else if ( rv > high_speed )
1271 return VIK_TRW_LAYER_TRACK_GC_FAST;
1273 return VIK_TRW_LAYER_TRACK_GC_AVER;
1276 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1279 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1281 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1282 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1283 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1284 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1287 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1289 /* TODO: this function is a mess, get rid of any redundancy */
1290 GList *list = track->trackpoints;
1292 gboolean useoldvals = TRUE;
1294 gboolean drawpoints;
1296 gboolean drawelevation;
1297 gdouble min_alt, max_alt, alt_diff = 0;
1299 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1300 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1303 if ( dp->vtl->drawelevation )
1305 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1306 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1307 alt_diff = max_alt - min_alt;
1310 if ( ! track->visible )
1313 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1314 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1315 trw_layer_draw_track ( id, track, dp, TRUE );
1317 if ( draw_track_outline )
1318 drawpoints = drawstops = FALSE;
1320 drawpoints = dp->vtl->drawpoints;
1321 drawstops = dp->vtl->drawstops;
1324 gboolean drawing_highlight = FALSE;
1325 /* Current track - used for creation */
1326 if ( track == dp->vtl->current_track )
1327 main_gc = dp->vtl->current_track_gc;
1329 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1330 /* Draw all tracks of the layer in special colour */
1331 /* if track is member of selected layer or is the current selected track
1332 then draw in the highlight colour.
1333 NB this supercedes the drawmode */
1334 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1335 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1336 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1337 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1338 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1339 drawing_highlight = TRUE;
1342 if ( !drawing_highlight ) {
1343 // Still need to figure out the gc according to the drawing mode:
1344 switch ( dp->vtl->drawmode ) {
1345 case DRAWMODE_BY_TRACK:
1346 if ( dp->vtl->track_1color_gc )
1347 g_object_unref ( dp->vtl->track_1color_gc );
1348 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1349 main_gc = dp->vtl->track_1color_gc;
1352 // Mostly for DRAWMODE_ALL_SAME_COLOR
1353 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1354 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1361 int x, y, oldx, oldy;
1362 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1364 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1366 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1368 // Draw the first point as something a bit different from the normal points
1369 // ATM it's slightly bigger and a triangle
1371 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1372 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1378 gdouble average_speed = 0.0;
1379 gdouble low_speed = 0.0;
1380 gdouble high_speed = 0.0;
1381 // If necessary calculate these values - which is done only once per track redraw
1382 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1383 // the percentage factor away from the average speed determines transistions between the levels
1384 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1385 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1386 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1389 while ((list = g_list_next(list)))
1391 tp = VIK_TRACKPOINT(list->data);
1392 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1394 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1395 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1396 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1397 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1398 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1400 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1403 * If points are the same in display coordinates, don't draw.
1405 if ( useoldvals && x == oldx && y == oldy )
1407 // Still need to process points to ensure 'stops' are drawn if required
1408 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1409 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1410 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 );
1415 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1416 if ( drawpoints || dp->vtl->drawlines ) {
1417 // setup main_gc for both point and line drawing
1418 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1419 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
1423 if ( drawpoints && ! draw_track_outline )
1428 * The concept of drawing stops is that a trackpoint
1429 * that is if the next trackpoint has a timestamp far into
1430 * the future, we draw a circle of 6x trackpoint size,
1431 * instead of a rectangle of 2x trackpoint size.
1432 * This is drawn first so the trackpoint will be drawn on top
1435 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1436 /* Stop point. Draw 6x circle. Always in redish colour */
1437 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
1439 /* Regular point - draw 2x square. */
1440 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1443 /* Final point - draw 4x circle. */
1444 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 );
1447 if ((!tp->newsegment) && (dp->vtl->drawlines))
1450 /* UTM only: zone check */
1451 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1452 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1455 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1457 if ( draw_track_outline ) {
1458 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1462 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1464 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1466 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1471 tmp[1].y = oldy-FIXALTITUDE(list->data);
1473 tmp[2].y = y-FIXALTITUDE(list->next->data);
1478 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1479 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1481 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1482 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1484 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1489 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1490 // Draw an arrow at the mid point to show the direction of the track
1491 // Code is a rework from vikwindow::draw_ruler()
1492 gint midx = (oldx + x) / 2;
1493 gint midy = (oldy + y) / 2;
1495 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1496 // Avoid divide by zero and ensure at least 1 pixel big
1498 gdouble dx = (oldx - midx) / len;
1499 gdouble dy = (oldy - midy) / len;
1500 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1501 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1511 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1513 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1514 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1516 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1518 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1519 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
1523 * If points are the same in display coordinates, don't draw.
1525 if ( x != oldx || y != oldy )
1527 if ( draw_track_outline )
1528 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1530 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1536 * If points are the same in display coordinates, don't draw.
1538 if ( x != oldx && y != oldy )
1540 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1541 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1551 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1552 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1554 trw_layer_draw_track ( id, track, dp, FALSE );
1557 static void cached_pixbuf_free ( CachedPixbuf *cp )
1559 g_object_unref ( G_OBJECT(cp->pixbuf) );
1560 g_free ( cp->image );
1563 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1565 return strcmp ( cp->image, name );
1568 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1571 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1572 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1573 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1576 GdkPixbuf *sym = NULL;
1577 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1579 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1581 if ( wp->image && dp->vtl->drawimages )
1583 GdkPixbuf *pixbuf = NULL;
1586 if ( dp->vtl->image_alpha == 0)
1589 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1591 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1594 gchar *image = wp->image;
1595 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1596 if ( ! regularthumb )
1598 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1599 image = "\x12\x00"; /* this shouldn't occur naturally. */
1603 CachedPixbuf *cp = NULL;
1604 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1605 if ( dp->vtl->image_size == 128 )
1606 cp->pixbuf = regularthumb;
1609 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1610 g_assert ( cp->pixbuf );
1611 g_object_unref ( G_OBJECT(regularthumb) );
1613 cp->image = g_strdup ( image );
1615 /* needed so 'click picture' tool knows how big the pic is; we don't
1616 * store it in cp because they may have been freed already. */
1617 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1618 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1620 g_queue_push_head ( dp->vtl->image_cache, cp );
1621 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1622 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1624 pixbuf = cp->pixbuf;
1628 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1634 w = gdk_pixbuf_get_width ( pixbuf );
1635 h = gdk_pixbuf_get_height ( pixbuf );
1637 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1639 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1640 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1641 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1642 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
1643 // Highlighted - so draw a little border around the chosen one
1644 // single line seems a little weak so draw 2 of them
1645 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1646 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1647 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1648 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1651 if ( dp->vtl->image_alpha == 255 )
1652 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1654 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1656 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1660 /* DRAW ACTUAL DOT */
1661 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1662 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 );
1664 else if ( wp == dp->vtl->current_wp ) {
1665 switch ( dp->vtl->wp_symbol ) {
1666 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;
1667 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;
1668 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;
1669 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 );
1670 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 );
1674 switch ( dp->vtl->wp_symbol ) {
1675 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;
1676 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;
1677 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;
1678 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 );
1679 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;
1683 if ( dp->vtl->drawlabels )
1685 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1686 gint label_x, label_y;
1688 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1690 // Could this stored in the waypoint rather than recreating each pass?
1691 gchar *fsize = NULL;
1692 switch (dp->vtl->wp_font_size) {
1693 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1694 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1695 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1696 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1697 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1698 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1699 default: fsize = g_strdup ( "medium" ); break;
1702 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1704 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1705 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1707 // Fallback if parse failure
1708 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1710 g_free ( wp_label_markup );
1713 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1714 label_x = x - width/2;
1716 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1718 label_y = y - dp->vtl->wp_size - height - 2;
1720 /* if highlight mode on, then draw background text in highlight colour */
1721 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1722 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1723 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1724 wp == vik_window_get_selected_waypoint ( dp->vw ) )
1725 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1727 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1730 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1732 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1737 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1739 static struct DrawingParams dp;
1740 g_assert ( l != NULL );
1742 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1744 if ( l->tracks_visible )
1745 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1747 if ( l->routes_visible )
1748 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1750 if (l->waypoints_visible)
1751 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1754 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1757 if ( vtl->track_bg_gc )
1759 g_object_unref ( vtl->track_bg_gc );
1760 vtl->track_bg_gc = NULL;
1762 if ( vtl->track_1color_gc )
1764 g_object_unref ( vtl->track_1color_gc );
1765 vtl->track_1color_gc = NULL;
1767 if ( vtl->current_track_gc )
1769 g_object_unref ( vtl->current_track_gc );
1770 vtl->current_track_gc = NULL;
1772 if ( vtl->current_track_newpoint_gc )
1774 g_object_unref ( vtl->current_track_newpoint_gc );
1775 vtl->current_track_newpoint_gc = NULL;
1778 if ( ! vtl->track_gc )
1780 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1781 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1782 g_array_free ( vtl->track_gc, TRUE );
1783 vtl->track_gc = NULL;
1786 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1788 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1789 gint width = vtl->line_thickness;
1791 if ( vtl->track_gc )
1792 trw_layer_free_track_gcs ( vtl );
1794 if ( vtl->track_bg_gc )
1795 g_object_unref ( vtl->track_bg_gc );
1796 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1798 // Ensure new track drawing heeds line thickness setting
1799 // however always have a minium of 2, as 1 pixel is really narrow
1800 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1802 if ( vtl->current_track_gc )
1803 g_object_unref ( vtl->current_track_gc );
1804 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1805 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1807 // 'newpoint' gc is exactly the same as the current track gc
1808 if ( vtl->current_track_newpoint_gc )
1809 g_object_unref ( vtl->current_track_newpoint_gc );
1810 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1811 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1813 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1815 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1816 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1818 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1819 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1820 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1822 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
1824 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1827 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1829 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1830 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1832 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1833 /* early exit, as the rest is GUI related */
1837 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1838 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1840 gdk_color_parse ( "#000000", &(rv->track_color) ); // Black
1842 trw_layer_new_track_gcs ( rv, vp );
1844 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1845 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1846 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1847 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1849 rv->has_verified_thumbnails = FALSE;
1850 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1852 rv->wp_draw_symbols = TRUE;
1853 rv->wp_font_size = FS_MEDIUM;
1855 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1857 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1862 #define SMALL_ICON_SIZE 18
1864 * Can accept a null symbol, and may return null value
1866 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1868 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1869 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1870 // So needing a small icon for the treeview may need some resizing:
1871 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1872 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1876 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1878 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1880 GdkPixbuf *pixbuf = NULL;
1882 if ( track->has_color ) {
1883 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1884 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1885 // Here is some magic found to do the conversion
1886 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1887 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1888 ((track->color.green & 0xff00) << 8) |
1889 (track->color.blue & 0xff00);
1891 gdk_pixbuf_fill ( pixbuf, pixel );
1894 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1895 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
1897 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
1901 g_object_unref (pixbuf);
1903 *new_iter = *((GtkTreeIter *) pass_along[1]);
1904 if ( track->is_route )
1905 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1907 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1909 if ( ! track->visible )
1910 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1913 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1915 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1917 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1918 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 );
1920 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 );
1923 *new_iter = *((GtkTreeIter *) pass_along[1]);
1924 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1926 if ( ! wp->visible )
1927 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1930 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1932 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1933 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1935 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1939 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1941 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1942 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1944 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1948 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1950 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1951 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1953 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1957 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1960 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1962 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1963 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1964 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1966 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1969 if ( g_hash_table_size (vtl->routes) > 0 ) {
1971 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
1973 pass_along[0] = &(vtl->routes_iter);
1974 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
1976 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
1978 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
1981 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
1982 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
1984 pass_along[0] = &(vtl->waypoints_iter);
1985 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1987 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1989 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1994 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1998 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1999 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2000 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2001 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2003 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2005 return (t->visible ^= 1);
2009 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2011 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2013 return (t->visible ^= 1);
2017 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2019 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2021 return (t->visible ^= 1);
2030 * Return a property about tracks for this layer
2032 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2034 return vtl->line_thickness;
2037 // Structure to hold multiple track information for a layer
2046 * Build up layer multiple track information via updating the tooltip_tracks structure
2048 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2050 tt->length = tt->length + vik_track_get_length (tr);
2052 // Ensure times are available
2053 if ( tr->trackpoints &&
2054 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2055 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2058 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2059 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2061 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2062 // Hence initialize to the first 'proper' value
2063 if ( tt->start_time == 0 )
2064 tt->start_time = t1;
2065 if ( tt->end_time == 0 )
2068 // Update find the earliest / last times
2069 if ( t1 < tt->start_time )
2070 tt->start_time = t1;
2071 if ( t2 > tt->end_time )
2074 // Keep track of total time
2075 // there maybe gaps within a track (eg segments)
2076 // but this should be generally good enough for a simple indicator
2077 tt->duration = tt->duration + (int)(t2-t1);
2082 * Generate tooltip text for the layer.
2083 * This is relatively complicated as it considers information for
2084 * no tracks, a single track or multiple tracks
2085 * (which may or may not have timing information)
2087 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2098 static gchar tmp_buf[128];
2101 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2103 // Safety check - I think these should always be valid
2104 if ( vtl->tracks && vtl->waypoints ) {
2105 tooltip_tracks tt = { 0.0, 0, 0 };
2106 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2108 GDate* gdate_start = g_date_new ();
2109 g_date_set_time_t (gdate_start, tt.start_time);
2111 GDate* gdate_end = g_date_new ();
2112 g_date_set_time_t (gdate_end, tt.end_time);
2114 if ( g_date_compare (gdate_start, gdate_end) ) {
2115 // Dates differ so print range on separate line
2116 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2117 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2118 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2121 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2122 if ( tt.start_time != 0 )
2123 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2127 if ( tt.length > 0.0 ) {
2128 gdouble len_in_units;
2130 // Setup info dependent on distance units
2131 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2132 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2133 len_in_units = VIK_METERS_TO_MILES(tt.length);
2136 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2137 len_in_units = tt.length/1000.0;
2140 // Timing information if available
2142 if ( tt.duration > 0 ) {
2143 g_snprintf (tbuf1, sizeof(tbuf1),
2144 _(" in %d:%02d hrs:mins"),
2145 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2147 g_snprintf (tbuf2, sizeof(tbuf2),
2148 _("\n%sTotal Length %.1f %s%s"),
2149 tbuf3, len_in_units, tbuf4, tbuf1);
2152 // Put together all the elements to form compact tooltip text
2153 g_snprintf (tmp_buf, sizeof(tmp_buf),
2154 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2155 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2157 g_date_free (gdate_start);
2158 g_date_free (gdate_end);
2165 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2169 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2171 // Very simple tooltip - may expand detail in the future...
2172 static gchar tmp_buf[32];
2173 g_snprintf (tmp_buf, sizeof(tmp_buf),
2175 g_hash_table_size (l->tracks));
2179 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2181 // Very simple tooltip - may expand detail in the future...
2182 static gchar tmp_buf[32];
2183 g_snprintf (tmp_buf, sizeof(tmp_buf),
2185 g_hash_table_size (l->routes));
2190 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2191 // Same tooltip for a route
2192 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2195 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2196 tr = g_hash_table_lookup ( l->tracks, sublayer );
2198 tr = g_hash_table_lookup ( l->routes, sublayer );
2201 // Could be a better way of handling strings - but this works...
2202 gchar time_buf1[20];
2203 gchar time_buf2[20];
2204 time_buf1[0] = '\0';
2205 time_buf2[0] = '\0';
2206 static gchar tmp_buf[100];
2207 // Compact info: Short date eg (11/20/99), duration and length
2208 // Hopefully these are the things that are most useful and so promoted into the tooltip
2209 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2210 // %x The preferred date representation for the current locale without the time.
2211 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2212 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2213 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2215 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2218 // Get length and consider the appropriate distance units
2219 gdouble tr_len = vik_track_get_length(tr);
2220 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2221 switch (dist_units) {
2222 case VIK_UNITS_DISTANCE_KILOMETRES:
2223 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2225 case VIK_UNITS_DISTANCE_MILES:
2226 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2235 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2237 // Very simple tooltip - may expand detail in the future...
2238 static gchar tmp_buf[32];
2239 g_snprintf (tmp_buf, sizeof(tmp_buf),
2241 g_hash_table_size (l->waypoints));
2245 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2247 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2248 // NB It's OK to return NULL
2259 * Function to show basic track point information on the statusbar
2261 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2264 switch (a_vik_get_units_height ()) {
2265 case VIK_UNITS_HEIGHT_FEET:
2266 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2269 //VIK_UNITS_HEIGHT_METRES:
2270 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2275 if ( trkpt->has_timestamp ) {
2276 // Compact date time format
2277 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2281 // Position is put later on, as this bit may not be seen if the display is not big enough,
2282 // one can easily use the current pointer position to see this if needed
2283 gchar *lat = NULL, *lon = NULL;
2284 static struct LatLon ll;
2285 vik_coord_to_latlon (&(trkpt->coord), &ll);
2286 a_coords_latlon_to_string ( &ll, &lat, &lon );
2289 // Again is put later on, as this bit may not be seen if the display is not big enough
2290 // trackname can be seen from the treeview (when enabled)
2291 // Also name could be very long to not leave room for anything else
2294 if ( vtl->current_tp_track ) {
2295 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2298 // Combine parts to make overall message
2299 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2300 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2307 * Function to show basic waypoint information on the statusbar
2309 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2312 switch (a_vik_get_units_height ()) {
2313 case VIK_UNITS_HEIGHT_FEET:
2314 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2317 //VIK_UNITS_HEIGHT_METRES:
2318 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2322 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2323 // one can easily use the current pointer position to see this if needed
2324 gchar *lat = NULL, *lon = NULL;
2325 static struct LatLon ll;
2326 vik_coord_to_latlon (&(wpt->coord), &ll);
2327 a_coords_latlon_to_string ( &ll, &lat, &lon );
2329 // Combine parts to make overall message
2332 // Add comment if available
2333 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2335 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2336 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2343 * General layer selection function, find out which bit is selected and take appropriate action
2345 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2348 l->current_wp = NULL;
2349 l->current_wp_id = NULL;
2350 trw_layer_cancel_current_tp ( l, FALSE );
2353 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2357 case VIK_TREEVIEW_TYPE_LAYER:
2359 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2360 /* Mark for redraw */
2365 case VIK_TREEVIEW_TYPE_SUBLAYER:
2369 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2371 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2372 /* Mark for redraw */
2376 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2378 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2379 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2380 /* Mark for redraw */
2384 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2386 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2387 /* Mark for redraw */
2391 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2393 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2394 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2395 /* Mark for redraw */
2399 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2401 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2402 /* Mark for redraw */
2406 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2408 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2410 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2411 // Show some waypoint info
2412 set_statusbar_msg_info_wpt ( l, wpt );
2413 /* Mark for redraw */
2420 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2429 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2434 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2439 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2444 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2446 return l->waypoints;
2450 * ATM use a case sensitive find
2451 * Finds the first one
2453 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2455 if ( wp && wp->name )
2456 if ( ! strcmp ( wp->name, name ) )
2462 * Get waypoint by name - not guaranteed to be unique
2463 * Finds the first one
2465 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2467 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2471 * ATM use a case sensitive find
2472 * Finds the first one
2474 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2476 if ( trk && trk->name )
2477 if ( ! strcmp ( trk->name, name ) )
2483 * Get track by name - not guaranteed to be unique
2484 * Finds the first one
2486 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2488 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2492 * Get route by name - not guaranteed to be unique
2493 * Finds the first one
2495 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2497 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2500 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] )
2502 static VikCoord fixme;
2503 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2504 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2505 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2506 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2507 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2508 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2509 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2510 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2511 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2514 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2516 GList *tr = trk->trackpoints;
2517 static VikCoord fixme;
2521 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2522 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2523 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2524 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2525 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2526 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2527 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2528 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2529 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2534 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2536 // Continually reuse maxmin to find the latest maximum and minimum values
2537 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2538 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2539 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2542 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2544 /* 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... */
2545 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2546 trw_layer_find_maxmin (vtl, maxmin);
2547 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2551 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2552 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2557 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2560 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2561 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2563 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2566 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2568 /* First set the center [in case previously viewing from elsewhere] */
2569 /* Then loop through zoom levels until provided positions are in view */
2570 /* This method is not particularly fast - but should work well enough */
2571 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2573 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2574 vik_viewport_set_center_coord ( vvp, &coord );
2576 /* Convert into definite 'smallest' and 'largest' positions */
2577 struct LatLon minmin;
2578 if ( maxmin[0].lat < maxmin[1].lat )
2579 minmin.lat = maxmin[0].lat;
2581 minmin.lat = maxmin[1].lat;
2583 struct LatLon maxmax;
2584 if ( maxmin[0].lon > maxmin[1].lon )
2585 maxmax.lon = maxmin[0].lon;
2587 maxmax.lon = maxmin[1].lon;
2589 /* Never zoom in too far - generally not that useful, as too close ! */
2590 /* Always recalculate the 'best' zoom level */
2592 vik_viewport_set_zoom ( vvp, zoom );
2594 gdouble min_lat, max_lat, min_lon, max_lon;
2595 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2596 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2597 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2598 /* NB I think the logic used in this test to determine if the bounds is within view
2599 fails if track goes across 180 degrees longitude.
2600 Hopefully that situation is not too common...
2601 Mind you viking doesn't really do edge locations to well anyway */
2602 if ( min_lat < minmin.lat &&
2603 max_lat > minmin.lat &&
2604 min_lon < maxmax.lon &&
2605 max_lon > maxmax.lon )
2606 /* Found within zoom level */
2611 vik_viewport_set_zoom ( vvp, zoom );
2615 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2617 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2618 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2619 trw_layer_find_maxmin (vtl, maxmin);
2620 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2623 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2628 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2630 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])) ) ) {
2631 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2634 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2637 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2639 GtkWidget *file_selector;
2641 gboolean failed = FALSE;
2642 file_selector = gtk_file_chooser_dialog_new (title,
2644 GTK_FILE_CHOOSER_ACTION_SAVE,
2645 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2646 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2648 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2650 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2652 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2653 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2655 gtk_widget_hide ( file_selector );
2656 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2661 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2663 gtk_widget_hide ( file_selector );
2664 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2669 gtk_widget_destroy ( file_selector );
2671 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2674 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2676 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2679 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2681 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2684 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2686 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2687 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2688 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2689 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2691 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2693 g_free ( auto_save_name );
2696 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2698 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2699 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2700 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2701 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2703 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2705 g_free ( auto_save_name );
2709 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2712 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2714 gchar *name_used = NULL;
2717 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2718 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2720 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2724 gchar *quoted_file = g_shell_quote ( name_used );
2725 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2726 g_free ( quoted_file );
2727 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2729 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2730 g_error_free ( err );
2734 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2735 //g_remove ( name_used );
2736 // Perhaps should be deleted when the program ends?
2737 // For now leave it to the user to delete it / use system temp cleanup methods.
2738 g_free ( name_used );
2742 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2744 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2747 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2749 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2752 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2754 gpointer layer_and_vlp[2];
2755 layer_and_vlp[0] = pass_along[0];
2756 layer_and_vlp[1] = pass_along[1];
2758 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2760 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2761 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2763 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2765 if ( !trk || !trk->name )
2768 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2769 gchar *auto_save_name = g_strdup ( trk->name );
2770 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2771 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2773 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2775 g_free ( auto_save_name );
2779 VikWaypoint *wp; // input
2780 gpointer uuid; // output
2783 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2785 wpu_udata *user_data = udata;
2786 if ( wp == user_data->wp ) {
2787 user_data->uuid = id;
2793 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2795 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2796 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2797 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2799 GTK_RESPONSE_REJECT,
2801 GTK_RESPONSE_ACCEPT,
2804 GtkWidget *label, *entry;
2805 label = gtk_label_new(_("Waypoint Name:"));
2806 entry = gtk_entry_new();
2808 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2809 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2810 gtk_widget_show_all ( label );
2811 gtk_widget_show_all ( entry );
2813 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2815 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2817 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2818 // Find *first* wp with the given name
2819 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2822 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2825 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2826 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2828 // Find and select on the side panel
2833 // Hmmm, want key of it
2834 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2836 if ( wpf && udata.uuid ) {
2837 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2838 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2847 gtk_widget_destroy ( dia );
2850 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2852 gchar *default_name = highest_wp_number_get(vtl);
2853 VikWaypoint *wp = vik_waypoint_new();
2854 gchar *returned_name;
2856 wp->coord = *def_coord;
2858 // Attempt to auto set height if DEM data is available
2859 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2860 if ( elev != VIK_DEM_INVALID_ELEVATION )
2861 wp->altitude = (gdouble)elev;
2863 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2865 if ( returned_name )
2868 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2869 g_free (default_name);
2870 g_free (returned_name);
2873 g_free (default_name);
2874 vik_waypoint_free(wp);
2878 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2880 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2881 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2882 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2883 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2884 VikViewport *vvp = vik_window_viewport(vw);
2886 // Note the order is max part first then min part - thus reverse order of use in min_max function:
2887 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
2888 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2889 vik_layers_panel_emit_update ( vlp );
2892 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2894 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2895 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2896 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2898 trw_layer_find_maxmin (vtl, maxmin);
2899 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2900 vik_layers_panel_emit_update ( vlp );
2903 #ifdef VIK_CONFIG_GEOTAG
2904 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2906 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2908 // Update directly - not changing the mtime
2909 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2912 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2914 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2917 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2921 * Use code in separate file for this feature as reasonably complex
2923 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2925 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2926 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2927 // Unset so can be reverified later if necessary
2928 vtl->has_verified_thumbnails = FALSE;
2930 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2936 static void trw_layer_geotagging ( gpointer lav[2] )
2938 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2939 // Unset so can be reverified later if necessary
2940 vtl->has_verified_thumbnails = FALSE;
2942 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2949 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2952 * Acquire into this TRW Layer straight from GPS Device
2954 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2956 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2957 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2958 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2959 VikViewport *vvp = vik_window_viewport(vw);
2961 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2962 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2965 #ifdef VIK_CONFIG_GOOGLE
2967 * Acquire into this TRW Layer from Google Directions
2969 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2971 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2972 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2973 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2974 VikViewport *vvp = vik_window_viewport(vw);
2976 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2980 #ifdef VIK_CONFIG_OPENSTREETMAP
2982 * Acquire into this TRW Layer from OSM
2984 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2986 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2987 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2988 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2989 VikViewport *vvp = vik_window_viewport(vw);
2991 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2995 * Acquire into this TRW Layer from OSM for 'My' Traces
2997 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
2999 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3000 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3001 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3002 VikViewport *vvp = vik_window_viewport(vw);
3004 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface );
3008 #ifdef VIK_CONFIG_GEOCACHES
3010 * Acquire into this TRW Layer from Geocaching.com
3012 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3014 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3015 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3016 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3017 VikViewport *vvp = vik_window_viewport(vw);
3019 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
3023 #ifdef VIK_CONFIG_GEOTAG
3025 * Acquire into this TRW Layer from images
3027 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3029 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3030 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3031 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3032 VikViewport *vvp = vik_window_viewport(vw);
3034 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3035 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
3037 // Reverify thumbnails as they may have changed
3038 vtl->has_verified_thumbnails = FALSE;
3039 trw_layer_verify_thumbnails ( vtl, NULL );
3043 static void trw_layer_gps_upload ( gpointer lav[2] )
3045 gpointer pass_along[6];
3046 pass_along[0] = lav[0];
3047 pass_along[1] = lav[1];
3048 pass_along[2] = NULL; // No track - operate on the layer
3049 pass_along[3] = NULL;
3050 pass_along[4] = NULL;
3051 pass_along[5] = NULL;
3053 trw_layer_gps_upload_any ( pass_along );
3057 * If pass_along[3] is defined that this will upload just that track
3059 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3061 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3062 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3064 // May not actually get a track here as pass_along[2&3] can be null
3065 VikTrack *track = NULL;
3066 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3067 gboolean xfer_all = FALSE;
3069 if ( pass_along[2] ) {
3071 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3072 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3075 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3076 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3079 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3082 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3086 else if ( !pass_along[4] )
3087 xfer_all = TRUE; // i.e. whole layer
3089 if (track && !track->visible) {
3090 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3094 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3095 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3096 GTK_DIALOG_DESTROY_WITH_PARENT,
3098 GTK_RESPONSE_ACCEPT,
3100 GTK_RESPONSE_REJECT,
3103 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3104 GtkWidget *response_w = NULL;
3105 #if GTK_CHECK_VERSION (2, 20, 0)
3106 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3110 gtk_widget_grab_focus ( response_w );
3112 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3114 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3115 datasource_gps_clean_up ( dgs );
3116 gtk_widget_destroy ( dialog );
3120 // Get info from reused datasource dialog widgets
3121 gchar* protocol = datasource_gps_get_protocol ( dgs );
3122 gchar* port = datasource_gps_get_descriptor ( dgs );
3123 // NB don't free the above strings as they're references to values held elsewhere
3124 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3125 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3126 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3127 gboolean turn_off = datasource_gps_get_off ( dgs );
3129 gtk_widget_destroy ( dialog );
3131 // When called from the viewport - work the corresponding layerspanel:
3133 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3136 // Apply settings to transfer to the GPS device
3143 vik_layers_panel_get_viewport (vlp),
3152 * Acquire into this TRW Layer from any GPS Babel supported file
3154 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3156 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3157 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3158 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3159 VikViewport *vvp = vik_window_viewport(vw);
3161 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3164 static void trw_layer_new_wp ( gpointer lav[2] )
3166 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3167 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3168 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3169 instead return true if you want to update. */
3170 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 )
3171 vik_layers_panel_emit_update ( vlp );
3174 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3176 vtl->current_track = vik_track_new();
3177 vtl->current_track->visible = TRUE;
3178 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3179 // Create track with the preferred colour from the layer properties
3180 vtl->current_track->color = vtl->track_color;
3182 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3183 vtl->current_track->has_color = TRUE;
3184 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3187 static void trw_layer_new_track ( gpointer lav[2] )
3189 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3191 if ( ! vtl->current_track ) {
3192 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3193 new_track_create_common ( vtl, name );
3195 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3199 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3201 vtl->current_track = vik_track_new();
3202 vtl->current_track->visible = TRUE;
3203 vtl->current_track->is_route = TRUE;
3204 // By default make all routes red
3205 vtl->current_track->has_color = TRUE;
3206 gdk_color_parse ( "red", &vtl->current_track->color );
3207 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3210 static void trw_layer_new_route ( gpointer lav[2] )
3212 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3214 if ( ! vtl->current_track ) {
3215 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3216 new_route_create_common ( vtl, name );
3217 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3221 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3223 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3224 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3226 if ( g_hash_table_size (vtl->routes) > 0 ) {
3227 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3228 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3229 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3230 vik_layers_panel_emit_update ( vlp );
3235 static void trw_layer_finish_track ( gpointer lav[2] )
3237 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3238 vtl->current_track = NULL;
3239 vik_layer_emit_update ( VIK_LAYER(vtl) );
3242 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3244 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3245 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3247 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3248 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3249 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3250 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3251 vik_layers_panel_emit_update ( vlp );
3255 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3257 /* NB do not care if wp is visible or not */
3258 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3261 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3263 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3264 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3266 /* Only 1 waypoint - jump straight to it */
3267 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3268 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3269 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3271 /* If at least 2 waypoints - find center and then zoom to fit */
3272 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3274 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3275 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3276 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3279 vik_layers_panel_emit_update ( vlp );
3282 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3284 static gpointer pass_along[2];
3286 GtkWidget *export_submenu;
3287 pass_along[0] = vtl;
3288 pass_along[1] = vlp;
3290 item = gtk_menu_item_new();
3291 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3292 gtk_widget_show ( item );
3294 if ( vtl->current_track ) {
3295 if ( vtl->current_track->is_route )
3296 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3298 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3299 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3300 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3301 gtk_widget_show ( item );
3304 item = gtk_menu_item_new ();
3305 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3306 gtk_widget_show ( item );
3309 /* Now with icons */
3310 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3311 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3312 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3313 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3314 gtk_widget_show ( item );
3316 GtkWidget *view_submenu = gtk_menu_new();
3317 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3318 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3319 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3320 gtk_widget_show ( item );
3321 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3323 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3324 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3325 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3326 gtk_widget_show ( item );
3328 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3329 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3330 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3331 gtk_widget_show ( item );
3333 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3335 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3336 gtk_widget_show ( item );
3338 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3339 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3340 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3341 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3342 gtk_widget_show ( item );
3344 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3345 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3346 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3347 gtk_widget_show ( item );
3349 export_submenu = gtk_menu_new ();
3350 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3351 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3352 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3353 gtk_widget_show ( item );
3354 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3356 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3357 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3358 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3359 gtk_widget_show ( item );
3361 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3363 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3364 gtk_widget_show ( item );
3366 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3368 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3369 gtk_widget_show ( item );
3371 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3372 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3373 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3374 gtk_widget_show ( item );
3376 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3377 item = gtk_menu_item_new_with_mnemonic ( external1 );
3378 g_free ( external1 );
3379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3380 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3381 gtk_widget_show ( item );
3383 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3384 item = gtk_menu_item_new_with_mnemonic ( external2 );
3385 g_free ( external2 );
3386 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3387 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3388 gtk_widget_show ( item );
3390 GtkWidget *new_submenu = gtk_menu_new();
3391 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3392 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3393 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3394 gtk_widget_show(item);
3395 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3397 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3398 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3399 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3400 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3401 gtk_widget_show ( item );
3403 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3404 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3405 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3406 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3407 gtk_widget_show ( item );
3408 // Make it available only when a new track *not* already in progress
3409 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3411 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3412 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3413 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3414 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3415 gtk_widget_show ( item );
3416 // Make it available only when a new track *not* already in progress
3417 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3419 #ifdef VIK_CONFIG_GEOTAG
3420 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3421 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3422 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3423 gtk_widget_show ( item );
3426 GtkWidget *acquire_submenu = gtk_menu_new ();
3427 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3428 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3429 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3430 gtk_widget_show ( item );
3431 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3433 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3434 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3435 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3436 gtk_widget_show ( item );
3438 #ifdef VIK_CONFIG_GOOGLE
3439 item = gtk_menu_item_new_with_mnemonic ( _("From Google _Directions...") );
3440 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3441 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3442 gtk_widget_show ( item );
3445 #ifdef VIK_CONFIG_OPENSTREETMAP
3446 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3448 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3449 gtk_widget_show ( item );
3451 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3452 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3453 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3454 gtk_widget_show ( item );
3457 #ifdef VIK_CONFIG_GEONAMES
3458 GtkWidget *wikipedia_submenu = gtk_menu_new();
3459 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3460 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3461 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3462 gtk_widget_show(item);
3463 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3465 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3466 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3468 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3469 gtk_widget_show ( item );
3471 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3472 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3473 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3474 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3475 gtk_widget_show ( item );
3478 #ifdef VIK_CONFIG_GEOCACHES
3479 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3480 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3481 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3482 gtk_widget_show ( item );
3485 #ifdef VIK_CONFIG_GEOTAG
3486 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3487 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3488 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3489 gtk_widget_show ( item );
3492 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3493 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3494 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3495 gtk_widget_show ( item );
3497 GtkWidget *upload_submenu = gtk_menu_new ();
3498 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3499 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3500 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3501 gtk_widget_show ( item );
3502 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3504 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3507 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3508 gtk_widget_show ( item );
3510 #ifdef VIK_CONFIG_OPENSTREETMAP
3511 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3512 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3513 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3514 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3515 gtk_widget_show ( item );
3518 GtkWidget *delete_submenu = gtk_menu_new ();
3519 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3520 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3521 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3522 gtk_widget_show ( item );
3523 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3525 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3526 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3527 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3528 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3529 gtk_widget_show ( item );
3531 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3532 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3533 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3534 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3535 gtk_widget_show ( item );
3537 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3538 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3539 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3540 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3541 gtk_widget_show ( item );
3543 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3544 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3546 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3547 gtk_widget_show ( item );
3549 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3550 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3551 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3552 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3553 gtk_widget_show ( item );
3555 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3556 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3557 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3558 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3559 gtk_widget_show ( item );
3561 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3562 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3564 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3565 gtk_widget_show ( item );
3568 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3569 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3571 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3572 gtk_widget_show ( item );
3576 // Fake Waypoint UUIDs vi simple increasing integer
3577 static guint wp_uuid = 0;
3579 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3583 vik_waypoint_set_name (wp, name);
3585 if ( VIK_LAYER(vtl)->realized )
3587 // Do we need to create the sublayer:
3588 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3589 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3592 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3594 // Visibility column always needed for waypoints
3595 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3596 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 );
3598 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 );
3600 // Actual setting of visibility dependent on the waypoint
3601 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3603 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3606 highest_wp_number_add_wp(vtl, name);
3607 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3611 // Fake Track UUIDs vi simple increasing integer
3612 static guint tr_uuid = 0;
3614 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3618 vik_track_set_name (t, name);
3620 if ( VIK_LAYER(vtl)->realized )
3622 // Do we need to create the sublayer:
3623 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3624 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3627 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3628 // Visibility column always needed for tracks
3629 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3630 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 );
3632 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 );
3634 // Actual setting of visibility dependent on the track
3635 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3637 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3640 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3642 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3645 // Fake Route UUIDs vi simple increasing integer
3646 static guint rt_uuid = 0;
3648 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3652 vik_track_set_name (t, name);
3654 if ( VIK_LAYER(vtl)->realized )
3656 // Do we need to create the sublayer:
3657 if ( g_hash_table_size (vtl->routes) == 0 ) {
3658 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3661 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3662 // Visibility column always needed for tracks
3663 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3664 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 );
3666 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 );
3668 // Actual setting of visibility dependent on the track
3669 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3671 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3674 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3676 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3679 /* to be called whenever a track has been deleted or may have been changed. */
3680 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3682 if (vtl->current_tp_track == trk )
3683 trw_layer_cancel_current_tp ( vtl, FALSE );
3686 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3689 gchar *newname = g_strdup(name);
3694 switch ( sublayer_type ) {
3695 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3696 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3698 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3699 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3702 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3705 // If found a name already in use try adding 1 to it and we try again
3707 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3709 newname = new_newname;
3712 } while ( id != NULL);
3717 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3719 // No more uniqueness of name forced when loading from a file
3720 // This now makes this function a little redunant as we just flow the parameters through
3721 vik_trw_layer_add_waypoint ( vtl, name, wp );
3724 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3726 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3727 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3728 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3729 vik_track_free ( tr );
3730 vtl->route_finder_append = FALSE; /* this means we have added it */
3733 // No more uniqueness of name forced when loading from a file
3735 vik_trw_layer_add_route ( vtl, name, tr );
3737 vik_trw_layer_add_track ( vtl, name, tr );
3739 if ( vtl->route_finder_check_added_track ) {
3740 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3741 vtl->route_finder_added_track = tr;
3746 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3748 *l = g_list_append(*l, id);
3752 * Move an item from one TRW layer to another TRW layer
3754 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3756 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3757 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3759 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3761 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3762 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3763 vik_trw_layer_delete_track ( vtl_src, trk );
3766 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3767 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3769 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3771 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3772 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3773 vik_trw_layer_delete_route ( vtl_src, trk );
3776 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3777 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3779 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3781 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3782 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3783 trw_layer_delete_waypoint ( vtl_src, wp );
3787 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3789 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3790 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3792 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3793 GList *items = NULL;
3796 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3797 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3799 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3800 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3802 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3803 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3808 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3809 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3811 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3812 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3814 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3821 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3822 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3827 VikTrack *trk; // input
3828 gpointer uuid; // output
3831 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3833 trku_udata *user_data = udata;
3834 if ( trk == user_data->trk ) {
3835 user_data->uuid = id;
3841 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3843 gboolean was_visible = FALSE;
3845 if ( trk && trk->name ) {
3847 if ( trk == vtl->current_track ) {
3848 vtl->current_track = NULL;
3849 vtl->current_tp_track = NULL;
3850 vtl->current_tp_id = NULL;
3851 vtl->moving_tp = FALSE;
3854 was_visible = trk->visible;
3856 if ( trk == vtl->route_finder_current_track )
3857 vtl->route_finder_current_track = NULL;
3859 if ( trk == vtl->route_finder_added_track )
3860 vtl->route_finder_added_track = NULL;
3866 // Hmmm, want key of it
3867 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3869 if ( trkf && udata.uuid ) {
3870 /* could be current_tp, so we have to check */
3871 trw_layer_cancel_tps_of_track ( vtl, trk );
3873 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3876 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3877 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3878 g_hash_table_remove ( vtl->tracks, udata.uuid );
3880 // If last sublayer, then remove sublayer container
3881 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3882 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3890 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
3892 gboolean was_visible = FALSE;
3894 if ( trk && trk->name ) {
3896 if ( trk == vtl->current_track ) {
3897 vtl->current_track = NULL;
3898 vtl->current_tp_track = NULL;
3899 vtl->current_tp_id = NULL;
3900 vtl->moving_tp = FALSE;
3903 was_visible = trk->visible;
3905 if ( trk == vtl->route_finder_current_track )
3906 vtl->route_finder_current_track = NULL;
3908 if ( trk == vtl->route_finder_added_track )
3909 vtl->route_finder_added_track = NULL;
3915 // Hmmm, want key of it
3916 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
3918 if ( trkf && udata.uuid ) {
3919 /* could be current_tp, so we have to check */
3920 trw_layer_cancel_tps_of_track ( vtl, trk );
3922 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
3925 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3926 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
3927 g_hash_table_remove ( vtl->routes, udata.uuid );
3929 // If last sublayer, then remove sublayer container
3930 if ( g_hash_table_size (vtl->routes) == 0 ) {
3931 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
3939 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3941 gboolean was_visible = FALSE;
3943 if ( wp && wp->name ) {
3945 if ( wp == vtl->current_wp ) {
3946 vtl->current_wp = NULL;
3947 vtl->current_wp_id = NULL;
3948 vtl->moving_wp = FALSE;
3951 was_visible = wp->visible;
3957 // Hmmm, want key of it
3958 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3960 if ( wpf && udata.uuid ) {
3961 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3964 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3965 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3967 highest_wp_number_remove_wp(vtl, wp->name);
3968 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3970 // If last sublayer, then remove sublayer container
3971 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3972 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3982 // Only for temporary use by trw_layer_delete_waypoint_by_name
3983 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3985 wpu_udata *user_data = udata;
3986 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3987 user_data->uuid = id;
3994 * Delete a waypoint by the given name
3995 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3996 * as there be multiple waypoints with the same name
3998 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4001 // Fake a waypoint with the given name
4002 udata.wp = vik_waypoint_new ();
4003 vik_waypoint_set_name (udata.wp, name);
4004 // Currently only the name is used in this waypoint find function
4007 // Hmmm, want key of it
4008 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4010 vik_waypoint_free (udata.wp);
4012 if ( wpf && udata.uuid )
4013 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4019 VikTrack *trk; // input
4020 gpointer uuid; // output
4023 // Only for temporary use by trw_layer_delete_track_by_name
4024 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4026 tpu_udata *user_data = udata;
4027 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4028 user_data->uuid = id;
4035 * Delete a track by the given name
4036 * NOTE: ATM this will delete the first encountered Track with the specified name
4037 * as there may be multiple tracks with the same name within the specified hash table
4039 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4042 // Fake a track with the given name
4043 udata.trk = vik_track_new ();
4044 vik_track_set_name (udata.trk, name);
4045 // Currently only the name is used in this waypoint find function
4048 // Hmmm, want key of it
4049 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4051 vik_track_free (udata.trk);
4053 if ( trkf && udata.uuid ) {
4054 // This could be a little better written...
4055 if ( vtl->tracks == ht_tracks )
4056 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4057 if ( vtl->routes == ht_tracks )
4058 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4065 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4067 vik_treeview_item_delete (vt, it );
4070 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4073 vtl->current_track = NULL;
4074 vtl->route_finder_current_track = NULL;
4075 vtl->route_finder_added_track = NULL;
4076 if (vtl->current_tp_track)
4077 trw_layer_cancel_current_tp(vtl, FALSE);
4079 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4080 g_hash_table_remove_all(vtl->routes_iters);
4081 g_hash_table_remove_all(vtl->routes);
4083 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4085 vik_layer_emit_update ( VIK_LAYER(vtl) );
4088 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4091 vtl->current_track = NULL;
4092 vtl->route_finder_current_track = NULL;
4093 vtl->route_finder_added_track = NULL;
4094 if (vtl->current_tp_track)
4095 trw_layer_cancel_current_tp(vtl, FALSE);
4097 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4098 g_hash_table_remove_all(vtl->tracks_iters);
4099 g_hash_table_remove_all(vtl->tracks);
4101 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4103 vik_layer_emit_update ( VIK_LAYER(vtl) );
4106 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4108 vtl->current_wp = NULL;
4109 vtl->current_wp_id = NULL;
4110 vtl->moving_wp = FALSE;
4112 highest_wp_number_reset(vtl);
4114 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4115 g_hash_table_remove_all(vtl->waypoints_iters);
4116 g_hash_table_remove_all(vtl->waypoints);
4118 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4120 vik_layer_emit_update ( VIK_LAYER(vtl) );
4123 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4125 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4126 // Get confirmation from the user
4127 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4128 _("Are you sure you want to delete all tracks in %s?"),
4129 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4130 vik_trw_layer_delete_all_tracks (vtl);
4133 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4135 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4136 // Get confirmation from the user
4137 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4138 _("Are you sure you want to delete all routes in %s?"),
4139 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4140 vik_trw_layer_delete_all_routes (vtl);
4143 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4145 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4146 // Get confirmation from the user
4147 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4148 _("Are you sure you want to delete all waypoints in %s?"),
4149 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4150 vik_trw_layer_delete_all_waypoints (vtl);
4153 static void trw_layer_delete_item ( gpointer pass_along[6] )
4155 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4156 gboolean was_visible = FALSE;
4157 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4159 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4160 if ( wp && wp->name ) {
4161 if ( GPOINTER_TO_INT ( pass_along[4]) )
4162 // Get confirmation from the user
4163 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4164 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4165 _("Are you sure you want to delete the waypoint \"%s\""),
4168 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4171 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4173 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4174 if ( trk && trk->name ) {
4175 if ( GPOINTER_TO_INT ( pass_along[4]) )
4176 // Get confirmation from the user
4177 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4178 _("Are you sure you want to delete the track \"%s\""),
4181 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4186 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4187 if ( trk && trk->name ) {
4188 if ( GPOINTER_TO_INT ( pass_along[4]) )
4189 // Get confirmation from the user
4190 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4191 _("Are you sure you want to delete the route \"%s\""),
4194 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4198 vik_layer_emit_update ( VIK_LAYER(vtl) );
4202 static void trw_layer_properties_item ( gpointer pass_along[7] )
4204 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4205 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4207 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4209 if ( wp && wp->name )
4211 gboolean updated = FALSE;
4212 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
4214 if ( updated && wp->symbol && pass_along[6] )
4215 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4217 if ( updated && VIK_LAYER(vtl)->visible )
4218 vik_layer_emit_update ( VIK_LAYER(vtl) );
4224 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4225 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4227 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4229 if ( tr && tr->name )
4231 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4234 pass_along[1], /* vlp */
4235 pass_along[5], /* vvp */
4236 pass_along[6]); /* iter */
4242 * Update the treeview of the track id - primarily to update the icon
4244 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4250 gpointer *trkf = NULL;
4251 if ( trk->is_route )
4252 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4254 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4256 if ( trkf && udata.uuid ) {
4258 GtkTreeIter *iter = NULL;
4259 if ( trk->is_route )
4260 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4262 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4265 // TODO: Make this a function
4266 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4267 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4268 ((trk->color.green & 0xff00) << 8) |
4269 (trk->color.blue & 0xff00);
4270 gdk_pixbuf_fill ( pixbuf, pixel );
4271 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4272 g_object_unref (pixbuf);
4279 Parameter 1 -> VikLayersPanel
4280 Parameter 2 -> VikLayer
4281 Parameter 3 -> VikViewport
4283 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4286 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4287 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4290 /* since vlp not set, vl & vvp should be valid instead! */
4292 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4293 vik_layer_emit_update ( VIK_LAYER(vl) );
4298 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4300 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4302 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4303 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4305 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4307 if ( track && track->trackpoints )
4308 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4311 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4313 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4315 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4316 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4318 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4320 if ( track && track->trackpoints )
4322 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4324 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4325 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4326 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4327 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4328 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4332 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4334 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4336 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4337 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4339 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4344 // Converting a track to a route can be a bit more complicated,
4345 // so give a chance to change our minds:
4346 if ( !trk->is_route &&
4347 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4348 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4350 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4351 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4356 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4359 trk_copy->is_route = !trk_copy->is_route;
4361 // ATM can't set name to self - so must create temporary copy
4362 gchar *name = g_strdup ( trk_copy->name );
4364 // Delete old one and then add new one
4365 if ( trk->is_route ) {
4366 vik_trw_layer_delete_route ( vtl, trk );
4367 vik_trw_layer_add_track ( vtl, name, trk_copy );
4370 // Extra route conversion bits...
4371 vik_track_merge_segments ( trk_copy );
4372 vik_track_to_routepoints ( trk_copy );
4374 vik_trw_layer_delete_track ( vtl, trk );
4375 vik_trw_layer_add_route ( vtl, name, trk_copy );
4379 // Update in case color of track / route changes when moving between sublayers
4380 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4384 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4386 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4388 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4389 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4391 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4396 vtl->current_track = track;
4397 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);
4399 if ( track->trackpoints )
4400 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4403 #ifdef VIK_CONFIG_GOOGLE
4405 * extend a track using route finder
4407 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4409 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4410 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4413 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4415 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4416 vtl->route_finder_coord = last_coord;
4417 vtl->route_finder_current_track = track;
4418 vtl->route_finder_started = TRUE;
4420 if ( track->trackpoints )
4421 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4426 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4428 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4429 /* Also warn if overwrite old elevation data */
4430 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4432 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4433 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4435 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4438 vik_track_apply_dem_data ( track );
4441 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4443 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4445 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4446 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4448 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4453 GList *trps = track->trackpoints;
4456 trps = g_list_last(trps);
4457 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4460 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4462 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4464 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4465 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4467 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4472 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4475 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4478 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4480 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4482 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4483 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4485 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4490 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4493 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4496 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4498 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4500 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4501 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4503 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4508 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4511 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4515 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4517 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4519 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4521 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4522 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4524 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4526 if ( trk && trk->trackpoints )
4528 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4529 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4530 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4531 if ( pass_along[1] )
4532 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4534 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4538 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4540 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4541 trw_layer_tpwin_init ( vtl );
4544 /*************************************
4545 * merge/split by time routines
4546 *************************************/
4548 /* called for each key in track hash table.
4549 * If the current track has the same time stamp type, add it to the result,
4550 * except the one pointed by "exclude".
4551 * set exclude to NULL if there is no exclude to check.
4552 * Note that the result is in reverse (for performance reasons).
4557 gboolean with_timestamps;
4559 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4561 twt_udata *user_data = udata;
4562 VikTrackpoint *p1, *p2;
4564 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4568 if (VIK_TRACK(value)->trackpoints) {
4569 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4570 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4572 if ( user_data->with_timestamps ) {
4573 if (!p1->has_timestamp || !p2->has_timestamp) {
4578 // Don't add tracks with timestamps when getting non timestamp tracks
4579 if (p1->has_timestamp || p2->has_timestamp) {
4585 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4588 /* called for each key in track hash table. if original track user_data[1] is close enough
4589 * to the passed one, add it to list in user_data[0]
4591 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4594 VikTrackpoint *p1, *p2;
4595 VikTrack *trk = VIK_TRACK(value);
4597 GList **nearby_tracks = ((gpointer *)user_data)[0];
4598 GList *tpoints = ((gpointer *)user_data)[1];
4601 * detect reasons for not merging, and return
4602 * if no reason is found not to merge, then do it.
4605 // Exclude the original track from the compiled list
4606 if (trk->trackpoints == tpoints) {
4610 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4611 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4613 if (trk->trackpoints) {
4614 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4615 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4617 if (!p1->has_timestamp || !p2->has_timestamp) {
4618 //g_print("no timestamp\n");
4622 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4623 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4624 if (! (abs(t1 - p2->timestamp) < threshold ||
4626 abs(p1->timestamp - t2) < threshold)
4633 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4636 /* comparison function used to sort tracks; a and b are hash table keys */
4637 /* Not actively used - can be restored if needed
4638 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4640 GHashTable *tracks = user_data;
4643 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4644 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4646 if (t1 < t2) return -1;
4647 if (t1 > t2) return 1;
4652 /* comparison function used to sort trackpoints */
4653 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4655 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4657 if (t1 < t2) return -1;
4658 if (t1 > t2) return 1;
4663 * comparison function which can be used to sort tracks or waypoints by name
4665 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4667 const gchar* namea = (const gchar*) a;
4668 const gchar* nameb = (const gchar*) b;
4669 if ( namea == NULL || nameb == NULL)
4672 // Same sort method as used in the vik_treeview_*_alphabetize functions
4673 return strcmp ( namea, nameb );
4677 * Attempt to merge selected track with other tracks specified by the user
4678 * Tracks to merge with must be of the same 'type' as the selected track -
4679 * either all with timestamps, or all without timestamps
4681 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4683 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4684 GList *other_tracks = NULL;
4685 GHashTable *ght_tracks;
4686 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4687 ght_tracks = vtl->routes;
4689 ght_tracks = vtl->tracks;
4691 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4696 if ( !track->trackpoints )
4700 udata.result = &other_tracks;
4701 udata.exclude = track->trackpoints;
4702 // Allow merging with 'similar' time type time tracks
4703 // i.e. either those times, or those without
4704 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4706 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4707 other_tracks = g_list_reverse(other_tracks);
4709 if ( !other_tracks ) {
4710 if ( udata.with_timestamps )
4711 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4713 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4717 // Sort alphabetically for user presentation
4718 // Convert into list of names for usage with dialog function
4719 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4720 GList *other_tracks_names = NULL;
4721 GList *iter = g_list_first ( other_tracks );
4723 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4724 iter = g_list_next ( iter );
4727 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4729 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4733 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4734 g_list_free(other_tracks);
4735 g_list_free(other_tracks_names);
4740 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4741 VikTrack *merge_track;
4742 if ( track->is_route )
4743 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4745 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4748 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4749 merge_track->trackpoints = NULL;
4750 if ( track->is_route )
4751 vik_trw_layer_delete_route (vtl, merge_track);
4753 vik_trw_layer_delete_track (vtl, merge_track);
4754 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4757 /* TODO: free data before free merge_list */
4758 for (l = merge_list; l != NULL; l = g_list_next(l))
4760 g_list_free(merge_list);
4761 vik_layer_emit_update( VIK_LAYER(vtl) );
4765 // c.f. trw_layer_sorted_track_id_by_name_list
4766 // but don't add the specified track to the list (normally current track)
4767 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4769 twt_udata *user_data = udata;
4772 if (trk->trackpoints == user_data->exclude) {
4776 // Sort named list alphabetically
4777 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4781 * Join - this allows combining 'tracks' and 'track routes'
4782 * i.e. doesn't care about whether tracks have consistent timestamps
4783 * ATM can only append one track at a time to the currently selected track
4785 static void trw_layer_append_track ( gpointer pass_along[6] )
4788 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4790 GHashTable *ght_tracks;
4791 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4792 ght_tracks = vtl->routes;
4794 ght_tracks = vtl->tracks;
4796 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4801 GList *other_tracks_names = NULL;
4803 // Sort alphabetically for user presentation
4804 // Convert into list of names for usage with dialog function
4805 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4807 udata.result = &other_tracks_names;
4808 udata.exclude = trk->trackpoints;
4810 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4812 // Note the limit to selecting one track only
4813 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4814 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4815 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4818 trk->is_route ? _("Append Route"): _("Append Track"),
4819 trk->is_route ? _("Select the route to append after the current route") :
4820 _("Select the track to append after the current track") );
4822 g_list_free(other_tracks_names);
4824 // It's a list, but shouldn't contain more than one other track!
4825 if ( append_list ) {
4827 for (l = append_list; l != NULL; l = g_list_next(l)) {
4828 // TODO: at present this uses the first track found by name,
4829 // which with potential multiple same named tracks may not be the one selected...
4830 VikTrack *append_track;
4831 if ( trk->is_route )
4832 append_track = vik_trw_layer_get_route ( vtl, l->data );
4834 append_track = vik_trw_layer_get_track ( vtl, l->data );
4836 if ( append_track ) {
4837 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4838 append_track->trackpoints = NULL;
4839 if ( trk->is_route )
4840 vik_trw_layer_delete_route (vtl, append_track);
4842 vik_trw_layer_delete_track (vtl, append_track);
4845 for (l = append_list; l != NULL; l = g_list_next(l))
4847 g_list_free(append_list);
4848 vik_layer_emit_update( VIK_LAYER(vtl) );
4853 * Very similar to trw_layer_append_track for joining
4854 * but this allows selection from the 'other' list
4855 * If a track is selected, then is shows routes and joins the selected one
4856 * If a route is selected, then is shows tracks and joins the selected one
4858 static void trw_layer_append_other ( gpointer pass_along[6] )
4861 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4863 GHashTable *ght_mykind, *ght_others;
4864 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4865 ght_mykind = vtl->routes;
4866 ght_others = vtl->tracks;
4869 ght_mykind = vtl->tracks;
4870 ght_others = vtl->routes;
4873 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
4878 GList *other_tracks_names = NULL;
4880 // Sort alphabetically for user presentation
4881 // Convert into list of names for usage with dialog function
4882 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4884 udata.result = &other_tracks_names;
4885 udata.exclude = trk->trackpoints;
4887 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4889 // Note the limit to selecting one track only
4890 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4891 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4892 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4895 trk->is_route ? _("Append Track"): _("Append Route"),
4896 trk->is_route ? _("Select the track to append after the current route") :
4897 _("Select the route to append after the current track") );
4899 g_list_free(other_tracks_names);
4901 // It's a list, but shouldn't contain more than one other track!
4902 if ( append_list ) {
4904 for (l = append_list; l != NULL; l = g_list_next(l)) {
4905 // TODO: at present this uses the first track found by name,
4906 // which with potential multiple same named tracks may not be the one selected...
4908 // Get FROM THE OTHER TYPE list
4909 VikTrack *append_track;
4910 if ( trk->is_route )
4911 append_track = vik_trw_layer_get_track ( vtl, l->data );
4913 append_track = vik_trw_layer_get_route ( vtl, l->data );
4915 if ( append_track ) {
4917 if ( !append_track->is_route &&
4918 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
4919 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
4921 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4922 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
4923 vik_track_merge_segments ( append_track );
4924 vik_track_to_routepoints ( append_track );
4931 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4932 append_track->trackpoints = NULL;
4934 // Delete copied which is FROM THE OTHER TYPE list
4935 if ( trk->is_route )
4936 vik_trw_layer_delete_track (vtl, append_track);
4938 vik_trw_layer_delete_route (vtl, append_track);
4941 for (l = append_list; l != NULL; l = g_list_next(l))
4943 g_list_free(append_list);
4944 vik_layer_emit_update( VIK_LAYER(vtl) );
4948 /* merge by segments */
4949 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4951 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4952 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4953 guint segments = vik_track_merge_segments ( trk );
4954 // NB currently no need to redraw as segments not actually shown on the display
4955 // However inform the user of what happened:
4957 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4958 g_snprintf(str, 64, tmp_str, segments);
4959 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4962 /* merge by time routine */
4963 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4965 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4969 GList *tracks_with_timestamp = NULL;
4970 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4971 if (orig_trk->trackpoints &&
4972 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4973 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4978 udata.result = &tracks_with_timestamp;
4979 udata.exclude = orig_trk->trackpoints;
4980 udata.with_timestamps = TRUE;
4981 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4982 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4984 if (!tracks_with_timestamp) {
4985 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4988 g_list_free(tracks_with_timestamp);
4990 static guint threshold_in_minutes = 1;
4991 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4992 _("Merge Threshold..."),
4993 _("Merge when time between tracks less than:"),
4994 &threshold_in_minutes)) {
4998 // keep attempting to merge all tracks until no merges within the time specified is possible
4999 gboolean attempt_merge = TRUE;
5000 GList *nearby_tracks = NULL;
5002 static gpointer params[3];
5004 while ( attempt_merge ) {
5006 // Don't try again unless tracks have changed
5007 attempt_merge = FALSE;
5009 trps = orig_trk->trackpoints;
5013 if (nearby_tracks) {
5014 g_list_free(nearby_tracks);
5015 nearby_tracks = NULL;
5018 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5019 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5021 /* g_print("Original track times: %d and %d\n", t1, t2); */
5022 params[0] = &nearby_tracks;
5023 params[1] = (gpointer)trps;
5024 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5026 /* get a list of adjacent-in-time tracks */
5027 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5030 GList *l = nearby_tracks;
5033 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5034 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5036 t1 = get_first_trackpoint(l)->timestamp;
5037 t2 = get_last_trackpoint(l)->timestamp;
5038 #undef get_first_trackpoint
5039 #undef get_last_trackpoint
5040 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5043 /* remove trackpoints from merged track, delete track */
5044 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
5045 VIK_TRACK(l->data)->trackpoints = NULL;
5046 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5048 // Tracks have changed, therefore retry again against all the remaining tracks
5049 attempt_merge = TRUE;
5054 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5057 g_list_free(nearby_tracks);
5058 vik_layer_emit_update( VIK_LAYER(vtl) );
5062 * Split a track at the currently selected trackpoint
5064 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5066 if ( !vtl->current_tpl )
5069 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5070 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5072 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5073 GList *newglist = g_list_alloc ();
5074 newglist->prev = NULL;
5075 newglist->next = vtl->current_tpl->next;
5076 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5077 tr->trackpoints = newglist;
5079 vtl->current_tpl->next->prev = newglist; /* end old track here */
5080 vtl->current_tpl->next = NULL;
5082 vtl->current_tpl = newglist; /* change tp to first of new track. */
5083 vtl->current_tp_track = tr;
5086 vik_trw_layer_add_route ( vtl, name, tr );
5088 vik_trw_layer_add_track ( vtl, name, tr );
5094 // Also need id of newly created track
5097 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5099 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5101 if ( trkf && udata.uuid )
5102 vtl->current_tp_id = udata.uuid;
5104 vtl->current_tp_id = NULL;
5106 vik_layer_emit_update(VIK_LAYER(vtl));
5111 /* split by time routine */
5112 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5114 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5115 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5116 GList *trps = track->trackpoints;
5118 GList *newlists = NULL;
5119 GList *newtps = NULL;
5120 static guint thr = 1;
5127 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5128 _("Split Threshold..."),
5129 _("Split when time between trackpoints exceeds:"),
5134 /* iterate through trackpoints, and copy them into new lists without touching original list */
5135 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5139 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5141 g_print("panic: ts < prev_ts: this should never happen!\n");
5144 if (ts - prev_ts > thr*60) {
5145 /* flush accumulated trackpoints into new list */
5146 newlists = g_list_append(newlists, g_list_reverse(newtps));
5150 /* accumulate trackpoint copies in newtps, in reverse order */
5151 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5153 iter = g_list_next(iter);
5156 newlists = g_list_append(newlists, g_list_reverse(newtps));
5159 /* put lists of trackpoints into tracks */
5161 // Only bother updating if the split results in new tracks
5162 if (g_list_length (newlists) > 1) {
5167 tr = vik_track_copy ( track, FALSE );
5168 tr->trackpoints = (GList *)(iter->data);
5170 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5171 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5172 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5173 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5175 iter = g_list_next(iter);
5177 // Remove original track and then update the display
5178 vik_trw_layer_delete_track (vtl, track);
5179 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5181 g_list_free(newlists);
5185 * Split a track by the number of points as specified by the user
5187 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5189 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5191 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5192 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5194 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5199 // Check valid track
5200 GList *trps = track->trackpoints;
5204 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5205 _("Split Every Nth Point"),
5206 _("Split on every Nth point:"),
5207 250, // Default value as per typical limited track capacity of various GPS devices
5211 // Was a valid number returned?
5217 GList *newlists = NULL;
5218 GList *newtps = NULL;
5223 /* accumulate trackpoint copies in newtps, in reverse order */
5224 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5226 if (count >= points) {
5227 /* flush accumulated trackpoints into new list */
5228 newlists = g_list_append(newlists, g_list_reverse(newtps));
5232 iter = g_list_next(iter);
5235 // If there is a remaining chunk put that into the new split list
5236 // This may well be the whole track if no split points were encountered
5238 newlists = g_list_append(newlists, g_list_reverse(newtps));
5241 /* put lists of trackpoints into tracks */
5243 // Only bother updating if the split results in new tracks
5244 if (g_list_length (newlists) > 1) {
5249 tr = vik_track_copy ( track, FALSE );
5250 tr->trackpoints = (GList *)(iter->data);
5252 if ( track->is_route ) {
5253 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5254 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5257 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5258 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5260 iter = g_list_next(iter);
5262 // Remove original track and then update the display
5263 if ( track->is_route )
5264 vik_trw_layer_delete_route (vtl, track);
5266 vik_trw_layer_delete_track (vtl, track);
5267 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5269 g_list_free(newlists);
5273 * Split a track at the currently selected trackpoint
5275 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5277 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5278 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5279 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5283 * Split a track by its segments
5284 * Routes do not have segments so don't call this for routes
5286 static void trw_layer_split_segments ( gpointer pass_along[6] )
5288 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5289 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5296 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5299 for ( i = 0; i < ntracks; i++ ) {
5301 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5302 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5307 // Remove original track
5308 vik_trw_layer_delete_track ( vtl, trk );
5309 vik_layer_emit_update ( VIK_LAYER(vtl) );
5312 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5315 /* end of split/merge routines */
5318 * Delete adjacent track points at the same position
5319 * AKA Delete Dulplicates on the Properties Window
5321 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5323 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5325 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5326 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5328 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5333 gulong removed = vik_track_remove_dup_points ( trk );
5335 // Track has been updated so update tps:
5336 trw_layer_cancel_tps_of_track ( vtl, trk );
5338 // Inform user how much was deleted as it's not obvious from the normal view
5340 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5341 g_snprintf(str, 64, tmp_str, removed);
5342 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5344 vik_layer_emit_update ( VIK_LAYER(vtl) );
5348 * Delete adjacent track points with the same timestamp
5349 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5351 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5353 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5355 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5356 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5358 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5363 gulong removed = vik_track_remove_same_time_points ( trk );
5365 // Track has been updated so update tps:
5366 trw_layer_cancel_tps_of_track ( vtl, trk );
5368 // Inform user how much was deleted as it's not obvious from the normal view
5370 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5371 g_snprintf(str, 64, tmp_str, removed);
5372 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5374 vik_layer_emit_update ( VIK_LAYER(vtl) );
5380 static void trw_layer_reverse ( gpointer pass_along[6] )
5382 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5384 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5385 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5387 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5392 // Check valid track
5393 GList *trps = track->trackpoints;
5397 vik_track_reverse ( track );
5399 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5403 * Similar to trw_layer_enum_item, but this uses a sorted method
5406 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5408 GList **list = (GList**)udata;
5409 // *list = g_list_prepend(*all, key); //unsorted method
5410 // Sort named list alphabetically
5411 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5416 * Now Waypoint specific sort
5418 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5420 GList **list = (GList**)udata;
5421 // Sort named list alphabetically
5422 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5426 * Track specific sort
5428 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5430 GList **list = (GList**)udata;
5431 // Sort named list alphabetically
5432 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5437 gboolean has_same_track_name;
5438 const gchar *same_track_name;
5439 } same_track_name_udata;
5441 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5443 const gchar* namea = (const gchar*) aa;
5444 const gchar* nameb = (const gchar*) bb;
5447 gint result = strcmp ( namea, nameb );
5449 if ( result == 0 ) {
5450 // Found two names the same
5451 same_track_name_udata *user_data = udata;
5452 user_data->has_same_track_name = TRUE;
5453 user_data->same_track_name = namea;
5456 // Leave ordering the same
5461 * Find out if any tracks have the same name in this hash table
5463 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5465 // Sort items by name, then compare if any next to each other are the same
5467 GList *track_names = NULL;
5468 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5471 if ( ! track_names )
5474 same_track_name_udata udata;
5475 udata.has_same_track_name = FALSE;
5477 // Use sort routine to traverse list comparing items
5478 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5479 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5480 // Still no tracks...
5484 return udata.has_same_track_name;
5488 * Force unqiue track names for the track table specified
5489 * Note the panel is a required parameter to enable the update of the names displayed
5490 * Specify if on tracks or else on routes
5492 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5494 // . Search list for an instance of repeated name
5495 // . get track of this name
5496 // . create new name
5497 // . rename track & update equiv. treeview iter
5498 // . repeat until all different
5500 same_track_name_udata udata;
5502 GList *track_names = NULL;
5503 udata.has_same_track_name = FALSE;
5504 udata.same_track_name = NULL;
5506 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5509 if ( ! track_names )
5512 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5514 // Still no tracks...
5515 if ( ! dummy_list1 )
5518 while ( udata.has_same_track_name ) {
5520 // Find a track with the same name
5523 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5525 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5529 g_critical("Houston, we've had a problem.");
5530 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5531 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5536 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5537 vik_track_set_name ( trk, newname );
5543 // Need want key of it for treeview update
5544 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5546 if ( trkf && udataU.uuid ) {
5550 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5552 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5555 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5556 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5557 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5562 // Start trying to find same names again...
5564 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5565 udata.has_same_track_name = FALSE;
5566 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5568 // No tracks any more - give up searching
5569 if ( ! dummy_list2 )
5570 udata.has_same_track_name = FALSE;
5574 vik_layers_panel_emit_update ( vlp );
5580 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5582 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5585 // Ensure list of track names offered is unique
5586 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5587 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5588 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5589 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5595 // Sort list alphabetically for better presentation
5596 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5599 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5603 // Get list of items to delete from the user
5604 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5607 _("Delete Selection"),
5608 _("Select tracks to delete"));
5611 // Delete requested tracks
5612 // since specificly requested, IMHO no need for extra confirmation
5613 if ( delete_list ) {
5615 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5616 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5617 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5619 g_list_free(delete_list);
5620 vik_layer_emit_update( VIK_LAYER(vtl) );
5627 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5629 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5632 // Ensure list of track names offered is unique
5633 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5634 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5635 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5636 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5642 // Sort list alphabetically for better presentation
5643 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5646 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5650 // Get list of items to delete from the user
5651 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5654 _("Delete Selection"),
5655 _("Select routes to delete") );
5658 // Delete requested routes
5659 // since specificly requested, IMHO no need for extra confirmation
5660 if ( delete_list ) {
5662 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5663 // This deletes first route it finds of that name (but uniqueness is enforced above)
5664 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5666 g_list_free(delete_list);
5667 vik_layer_emit_update( VIK_LAYER(vtl) );
5672 gboolean has_same_waypoint_name;
5673 const gchar *same_waypoint_name;
5674 } same_waypoint_name_udata;
5676 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5678 const gchar* namea = (const gchar*) aa;
5679 const gchar* nameb = (const gchar*) bb;
5682 gint result = strcmp ( namea, nameb );
5684 if ( result == 0 ) {
5685 // Found two names the same
5686 same_waypoint_name_udata *user_data = udata;
5687 user_data->has_same_waypoint_name = TRUE;
5688 user_data->same_waypoint_name = namea;
5691 // Leave ordering the same
5696 * Find out if any waypoints have the same name in this layer
5698 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5700 // Sort items by name, then compare if any next to each other are the same
5702 GList *waypoint_names = NULL;
5703 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5706 if ( ! waypoint_names )
5709 same_waypoint_name_udata udata;
5710 udata.has_same_waypoint_name = FALSE;
5712 // Use sort routine to traverse list comparing items
5713 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5714 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5715 // Still no waypoints...
5719 return udata.has_same_waypoint_name;
5723 * Force unqiue waypoint names for this layer
5724 * Note the panel is a required parameter to enable the update of the names displayed
5726 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5728 // . Search list for an instance of repeated name
5729 // . get waypoint of this name
5730 // . create new name
5731 // . rename waypoint & update equiv. treeview iter
5732 // . repeat until all different
5734 same_waypoint_name_udata udata;
5736 GList *waypoint_names = NULL;
5737 udata.has_same_waypoint_name = FALSE;
5738 udata.same_waypoint_name = NULL;
5740 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5743 if ( ! waypoint_names )
5746 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5748 // Still no waypoints...
5749 if ( ! dummy_list1 )
5752 while ( udata.has_same_waypoint_name ) {
5754 // Find a waypoint with the same name
5755 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5759 g_critical("Houston, we've had a problem.");
5760 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5761 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5766 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5767 vik_waypoint_set_name ( waypoint, newname );
5770 udataU.wp = waypoint;
5773 // Need want key of it for treeview update
5774 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5776 if ( wpf && udataU.uuid ) {
5778 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5781 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5782 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5783 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5788 // Start trying to find same names again...
5789 waypoint_names = NULL;
5790 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5791 udata.has_same_waypoint_name = FALSE;
5792 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5794 // No waypoints any more - give up searching
5795 if ( ! dummy_list2 )
5796 udata.has_same_waypoint_name = FALSE;
5800 vik_layers_panel_emit_update ( vlp );
5806 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5808 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5811 // Ensure list of waypoint names offered is unique
5812 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5813 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5814 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5815 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5821 // Sort list alphabetically for better presentation
5822 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5824 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5828 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5830 // Get list of items to delete from the user
5831 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5834 _("Delete Selection"),
5835 _("Select waypoints to delete"));
5838 // Delete requested waypoints
5839 // since specificly requested, IMHO no need for extra confirmation
5840 if ( delete_list ) {
5842 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5843 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5844 trw_layer_delete_waypoint_by_name (vtl, l->data);
5846 g_list_free(delete_list);
5847 vik_layer_emit_update( VIK_LAYER(vtl) );
5852 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5854 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5856 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5859 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5861 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5864 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
5865 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5869 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
5871 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5874 if ( !strncmp(wp->comment, "http", 4) ) {
5875 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
5876 } else if ( !strncmp(wp->description, "http", 4) ) {
5877 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
5881 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
5883 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5885 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
5887 // No actual change to the name supplied
5888 if (strcmp(newname, wp->name) == 0 )
5891 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5894 // An existing waypoint has been found with the requested name
5895 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5896 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
5901 // Update WP name and refresh the treeview
5902 vik_waypoint_set_name (wp, newname);
5904 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5905 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5908 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5913 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5915 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
5917 // No actual change to the name supplied
5918 if (strcmp(newname, trk->name) == 0)
5921 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
5924 // An existing track has been found with the requested name
5925 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5926 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
5930 // Update track name and refresh GUI parts
5931 vik_track_set_name (trk, newname);
5933 // Update any subwindows that could be displaying this track which has changed name
5934 // Only one Track Edit Window
5935 if ( l->current_tp_track == trk && l->tpwin ) {
5936 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5938 // Property Dialog of the track
5939 vik_trw_layer_propwin_update ( trk );
5941 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5942 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5945 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5950 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5952 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
5954 // No actual change to the name supplied
5955 if (strcmp(newname, trk->name) == 0)
5958 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
5961 // An existing track has been found with the requested name
5962 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5963 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
5967 // Update track name and refresh GUI parts
5968 vik_track_set_name (trk, newname);
5970 // Update any subwindows that could be displaying this track which has changed name
5971 // Only one Track Edit Window
5972 if ( l->current_tp_track == trk && l->tpwin ) {
5973 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5975 // Property Dialog of the track
5976 vik_trw_layer_propwin_update ( trk );
5978 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5979 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5982 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5989 static gboolean is_valid_geocache_name ( gchar *str )
5991 gint len = strlen ( str );
5992 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]));
5995 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
5997 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5998 a_acquire_set_filter_track ( trk );
6001 #ifdef VIK_CONFIG_GOOGLE
6002 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6004 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6005 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6008 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6010 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6012 gchar *escaped = uri_escape ( tr->comment );
6013 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6014 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6021 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6022 /* viewpoint is now available instead */
6023 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6025 static gpointer pass_along[8];
6027 gboolean rv = FALSE;
6030 pass_along[1] = vlp;
6031 pass_along[2] = GINT_TO_POINTER (subtype);
6032 pass_along[3] = sublayer;
6033 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6034 pass_along[5] = vvp;
6035 pass_along[6] = iter;
6036 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6038 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6042 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6044 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6045 gtk_widget_show ( item );
6047 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6048 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6049 if (tr && tr->property_dialog)
6050 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6052 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6053 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6054 if (tr && tr->property_dialog)
6055 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6058 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6059 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6060 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6061 gtk_widget_show ( item );
6063 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6065 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6066 gtk_widget_show ( item );
6068 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6069 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6070 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6071 gtk_widget_show ( item );
6073 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6075 gboolean separator_created = FALSE;
6077 /* could be a right-click using the tool */
6078 if ( vlp != NULL ) {
6079 item = gtk_menu_item_new ();
6080 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6081 gtk_widget_show ( item );
6083 separator_created = TRUE;
6085 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6086 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6087 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6088 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6089 gtk_widget_show ( item );
6092 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6094 if ( wp && wp->name ) {
6095 if ( is_valid_geocache_name ( wp->name ) ) {
6097 if ( !separator_created ) {
6098 item = gtk_menu_item_new ();
6099 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6100 gtk_widget_show ( item );
6101 separator_created = TRUE;
6104 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6106 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6107 gtk_widget_show ( item );
6111 if ( wp && wp->image )
6113 if ( !separator_created ) {
6114 item = gtk_menu_item_new ();
6115 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6116 gtk_widget_show ( item );
6117 separator_created = TRUE;
6120 // Set up image paramater
6121 pass_along[5] = wp->image;
6123 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6124 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
6125 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6126 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6127 gtk_widget_show ( item );
6129 #ifdef VIK_CONFIG_GEOTAG
6130 GtkWidget *geotag_submenu = gtk_menu_new ();
6131 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6133 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6134 gtk_widget_show ( item );
6135 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6137 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6139 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6140 gtk_widget_show ( item );
6142 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6143 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6144 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6145 gtk_widget_show ( item );
6151 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6152 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6153 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6154 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6155 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6156 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6157 gtk_widget_show ( item );
6164 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6165 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6166 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6167 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6168 gtk_widget_show ( item );
6169 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6170 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6171 gtk_widget_set_sensitive ( item, TRUE );
6173 gtk_widget_set_sensitive ( item, FALSE );
6176 item = gtk_menu_item_new ();
6177 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6178 gtk_widget_show ( item );
6181 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6184 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6185 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6187 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6188 gtk_widget_show ( item );
6191 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6193 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6194 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6196 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6197 gtk_widget_show ( item );
6199 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6200 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6201 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6202 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6203 gtk_widget_show ( item );
6205 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6206 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6208 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6209 gtk_widget_show ( item );
6211 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6212 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6213 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6214 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6215 gtk_widget_show ( item );
6218 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6222 if ( l->current_track && !l->current_track->is_route ) {
6223 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6225 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6226 gtk_widget_show ( item );
6228 item = gtk_menu_item_new ();
6229 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6230 gtk_widget_show ( item );
6233 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6234 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6235 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6236 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6237 gtk_widget_show ( item );
6239 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6240 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6241 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6242 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6243 gtk_widget_show ( item );
6244 // Make it available only when a new track *not* already in progress
6245 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6247 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6248 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6249 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6250 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6251 gtk_widget_show ( item );
6253 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6254 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6257 gtk_widget_show ( item );
6260 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6264 if ( l->current_track && l->current_track->is_route ) {
6265 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6266 // Reuse finish track method
6267 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6268 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6269 gtk_widget_show ( item );
6271 item = gtk_menu_item_new ();
6272 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6273 gtk_widget_show ( item );
6276 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6277 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6279 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6280 gtk_widget_show ( item );
6282 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6283 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6285 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6286 gtk_widget_show ( item );
6287 // Make it available only when a new track *not* already in progress
6288 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6290 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6291 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6292 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6293 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6294 gtk_widget_show ( item );
6296 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6297 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6299 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6300 gtk_widget_show ( item );
6303 GtkWidget *upload_submenu = gtk_menu_new ();
6305 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6307 item = gtk_menu_item_new ();
6308 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6309 gtk_widget_show ( item );
6311 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6312 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6313 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6314 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6315 if ( l->current_track ) {
6316 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6317 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6318 gtk_widget_show ( item );
6321 item = gtk_menu_item_new ();
6322 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6323 gtk_widget_show ( item );
6326 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6327 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6329 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6330 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6331 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6332 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6333 gtk_widget_show ( item );
6335 GtkWidget *goto_submenu;
6336 goto_submenu = gtk_menu_new ();
6337 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6338 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6339 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6340 gtk_widget_show ( item );
6341 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6343 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6344 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6345 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6346 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6347 gtk_widget_show ( item );
6349 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6350 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6351 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6352 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6353 gtk_widget_show ( item );
6355 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6356 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6357 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6358 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6359 gtk_widget_show ( item );
6361 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6362 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6363 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6364 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6365 gtk_widget_show ( item );
6367 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6368 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6369 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6370 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6371 gtk_widget_show ( item );
6373 // Routes don't have speeds
6374 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6375 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6376 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6378 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6379 gtk_widget_show ( item );
6382 GtkWidget *combine_submenu;
6383 combine_submenu = gtk_menu_new ();
6384 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6385 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6386 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6387 gtk_widget_show ( item );
6388 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6390 // Routes don't have times or segments...
6391 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6392 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6394 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6395 gtk_widget_show ( item );
6397 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6398 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6399 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6400 gtk_widget_show ( item );
6403 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6404 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6405 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6406 gtk_widget_show ( item );
6408 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6409 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6411 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6412 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6413 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6414 gtk_widget_show ( item );
6416 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6417 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6419 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6420 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6421 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6422 gtk_widget_show ( item );
6424 GtkWidget *split_submenu;
6425 split_submenu = gtk_menu_new ();
6426 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6427 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6428 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6429 gtk_widget_show ( item );
6430 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6432 // Routes don't have times or segments...
6433 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6434 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6435 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6436 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6437 gtk_widget_show ( item );
6439 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6440 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6442 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6443 gtk_widget_show ( item );
6446 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6448 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6449 gtk_widget_show ( item );
6451 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6452 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6453 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6454 gtk_widget_show ( item );
6455 // Make it available only when a trackpoint is selected.
6456 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6458 GtkWidget *delete_submenu;
6459 delete_submenu = gtk_menu_new ();
6460 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6461 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6462 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6463 gtk_widget_show ( item );
6464 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6466 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6468 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6469 gtk_widget_show ( item );
6471 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6473 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6474 gtk_widget_show ( item );
6476 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6477 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6479 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6480 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6481 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6482 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6483 gtk_widget_show ( item );
6485 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6487 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6488 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6490 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6491 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
6492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6493 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6494 gtk_widget_show ( item );
6497 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6498 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
6499 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6500 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6501 gtk_widget_show ( item );
6503 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6504 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6506 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6507 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6508 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6509 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6510 gtk_widget_show ( item );
6512 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6513 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6515 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6516 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6517 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6518 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6519 gtk_widget_show ( item );
6521 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6522 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6524 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6525 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6526 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6527 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6528 gtk_widget_show ( item );
6530 #ifdef VIK_CONFIG_GOOGLE
6531 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6532 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6533 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
6534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6535 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6536 gtk_widget_show ( item );
6540 // ATM can't upload a single waypoint but can do waypoints to a GPS
6541 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6542 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6543 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6544 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6545 gtk_widget_show ( item );
6546 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6548 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6549 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6550 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6551 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6552 gtk_widget_show ( item );
6556 #ifdef VIK_CONFIG_GOOGLE
6557 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6559 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6560 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6562 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6563 gtk_widget_show ( item );
6567 // Some things aren't usable with routes
6568 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6569 #ifdef VIK_CONFIG_OPENSTREETMAP
6570 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6571 // Convert internal pointer into actual track for usage outside this file
6572 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6573 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6574 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6575 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6576 gtk_widget_show ( item );
6579 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6580 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6581 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6582 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6583 gtk_widget_show ( item );
6585 /* ATM This function is only available via the layers panel, due to needing a vlp */
6587 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6588 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6589 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6591 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6592 gtk_widget_show ( item );
6596 #ifdef VIK_CONFIG_GEOTAG
6597 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6598 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6599 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6600 gtk_widget_show ( item );
6604 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6605 // Only show on viewport popmenu when a trackpoint is selected
6606 if ( ! vlp && l->current_tpl ) {
6608 item = gtk_menu_item_new ();
6609 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6610 gtk_widget_show ( item );
6612 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6613 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6614 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6615 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6616 gtk_widget_show ( item );
6623 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6626 if (!vtl->current_tpl)
6628 if (!vtl->current_tpl->next)
6631 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6632 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6634 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6637 VikTrackpoint *tp_new = vik_trackpoint_new();
6638 struct LatLon ll_current, ll_next;
6639 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6640 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6642 /* main positional interpolation */
6643 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6644 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6646 /* Now other properties that can be interpolated */
6647 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6649 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6650 /* Note here the division is applied to each part, then added
6651 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6652 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6653 tp_new->has_timestamp = TRUE;
6656 if (tp_current->speed != NAN && tp_next->speed != NAN)
6657 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6659 /* TODO - improve interpolation of course, as it may not be correct.
6660 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6661 [similar applies if value is in radians] */
6662 if (tp_current->course != NAN && tp_next->course != NAN)
6663 tp_new->speed = (tp_current->course + tp_next->course)/2;
6665 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6667 /* Insert new point into the trackpoints list after the current TP */
6668 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6670 // Otherwise try routes
6671 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6675 gint index = g_list_index ( trk->trackpoints, tp_current );
6677 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6682 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6688 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6692 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6694 if ( vtl->current_tpl )
6696 vtl->current_tpl = NULL;
6697 vtl->current_tp_track = NULL;
6698 vtl->current_tp_id = NULL;
6699 vik_layer_emit_update(VIK_LAYER(vtl));
6703 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6705 g_assert ( vtl->tpwin != NULL );
6706 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6707 trw_layer_cancel_current_tp ( vtl, TRUE );
6709 if ( vtl->current_tpl == NULL )
6712 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6714 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6715 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6717 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6719 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6721 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6727 // Find available adjacent trackpoint
6728 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6730 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6731 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6733 // Delete current trackpoint
6734 vik_trackpoint_free ( vtl->current_tpl->data );
6735 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6737 // Set to current to the available adjacent trackpoint
6738 vtl->current_tpl = new_tpl;
6740 // Reset dialog with the available adjacent trackpoint
6741 if ( vtl->current_tp_track )
6742 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6744 vik_layer_emit_update(VIK_LAYER(vtl));
6748 // Delete current trackpoint
6749 vik_trackpoint_free ( vtl->current_tpl->data );
6750 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6751 trw_layer_cancel_current_tp ( vtl, FALSE );
6754 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6756 if ( vtl->current_tp_track )
6757 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6758 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
6760 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6762 if ( vtl->current_tp_track )
6763 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6764 vik_layer_emit_update(VIK_LAYER(vtl));
6766 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6768 trw_layer_insert_tp_after_current_tp ( vtl );
6769 vik_layer_emit_update(VIK_LAYER(vtl));
6771 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6772 vik_layer_emit_update(VIK_LAYER(vtl));
6775 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
6779 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6780 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
6781 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
6782 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
6783 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
6785 if ( vtl->current_tpl )
6786 if ( vtl->current_tp_track )
6787 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6788 /* set layer name and TP data */
6791 /***************************************************************************
6793 ***************************************************************************/
6795 /*** Utility data structures and functions ****/
6799 gint closest_x, closest_y;
6800 gpointer *closest_wp_id;
6801 VikWaypoint *closest_wp;
6807 gint closest_x, closest_y;
6808 gpointer closest_track_id;
6809 VikTrackpoint *closest_tp;
6814 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
6820 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
6822 // If waypoint has an image then use the image size to select
6824 gint slackx, slacky;
6825 slackx = wp->image_width / 2;
6826 slacky = wp->image_height / 2;
6828 if ( x <= params->x + slackx && x >= params->x - slackx
6829 && y <= params->y + slacky && y >= params->y - slacky ) {
6830 params->closest_wp_id = id;
6831 params->closest_wp = wp;
6832 params->closest_x = x;
6833 params->closest_y = y;
6836 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
6837 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
6838 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6840 params->closest_wp_id = id;
6841 params->closest_wp = wp;
6842 params->closest_x = x;
6843 params->closest_y = y;
6847 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
6849 GList *tpl = t->trackpoints;
6858 tp = VIK_TRACKPOINT(tpl->data);
6860 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
6862 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
6863 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
6864 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6866 params->closest_track_id = id;
6867 params->closest_tp = tp;
6868 params->closest_tpl = tpl;
6869 params->closest_x = x;
6870 params->closest_y = y;
6876 // ATM: Leave this as 'Track' only.
6877 // Not overly bothered about having a snap to route trackpoint capability
6878 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6880 TPSearchParams params;
6884 params.closest_track_id = NULL;
6885 params.closest_tp = NULL;
6886 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6887 return params.closest_tp;
6890 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6892 WPSearchParams params;
6896 params.closest_wp = NULL;
6897 params.closest_wp_id = NULL;
6898 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6899 return params.closest_wp;
6903 // Some forward declarations
6904 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
6905 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
6906 static void marker_end_move ( tool_ed_t *t );
6909 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6913 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6915 // Here always allow snapping back to the original location
6916 // this is useful when one decides not to move the thing afterall
6917 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
6920 if ( event->state & GDK_CONTROL_MASK )
6922 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6924 new_coord = tp->coord;
6928 if ( event->state & GDK_SHIFT_MASK )
6930 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6932 new_coord = wp->coord;
6936 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6938 marker_moveto ( t, x, y );
6945 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6947 if ( t->holding && event->button == 1 )
6950 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6953 if ( event->state & GDK_CONTROL_MASK )
6955 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6957 new_coord = tp->coord;
6961 if ( event->state & GDK_SHIFT_MASK )
6963 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6965 new_coord = wp->coord;
6968 marker_end_move ( t );
6970 // Determine if working on a waypoint or a trackpoint
6971 if ( t->is_waypoint )
6972 vtl->current_wp->coord = new_coord;
6974 if ( vtl->current_tpl ) {
6975 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6978 if ( vtl->current_tp_track )
6979 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6984 vtl->current_wp = NULL;
6985 vtl->current_wp_id = NULL;
6986 trw_layer_cancel_current_tp ( vtl, FALSE );
6988 vik_layer_emit_update ( VIK_LAYER(vtl) );
6995 Returns true if a waypoint or track is found near the requested event position for this particular layer
6996 The item found is automatically selected
6997 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
6999 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7001 if ( event->button != 1 )
7004 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7007 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7010 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7012 if (vtl->waypoints_visible) {
7013 WPSearchParams wp_params;
7014 wp_params.vvp = vvp;
7015 wp_params.x = event->x;
7016 wp_params.y = event->y;
7017 wp_params.closest_wp_id = NULL;
7018 wp_params.closest_wp = NULL;
7020 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7022 if ( wp_params.closest_wp ) {
7025 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7027 // Too easy to move it so must be holding shift to start immediately moving it
7028 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7029 if ( event->state & GDK_SHIFT_MASK ||
7030 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7031 // Put into 'move buffer'
7032 // NB vvp & vw already set in tet
7033 tet->vtl = (gpointer)vtl;
7034 tet->is_waypoint = TRUE;
7036 marker_begin_move (tet, event->x, event->y);
7039 vtl->current_wp = wp_params.closest_wp;
7040 vtl->current_wp_id = wp_params.closest_wp_id;
7042 vik_layer_emit_update ( VIK_LAYER(vtl) );
7048 // Used for both track and route lists
7049 TPSearchParams tp_params;
7050 tp_params.vvp = vvp;
7051 tp_params.x = event->x;
7052 tp_params.y = event->y;
7053 tp_params.closest_track_id = NULL;
7054 tp_params.closest_tp = NULL;
7056 if (vtl->tracks_visible) {
7057 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7059 if ( tp_params.closest_tp ) {
7061 // Always select + highlight the track
7062 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7064 tet->is_waypoint = FALSE;
7066 // Select the Trackpoint
7067 // Can move it immediately when control held or it's the previously selected tp
7068 if ( event->state & GDK_CONTROL_MASK ||
7069 vtl->current_tpl == tp_params.closest_tpl ) {
7070 // Put into 'move buffer'
7071 // NB vvp & vw already set in tet
7072 tet->vtl = (gpointer)vtl;
7073 marker_begin_move (tet, event->x, event->y);
7076 vtl->current_tpl = tp_params.closest_tpl;
7077 vtl->current_tp_id = tp_params.closest_track_id;
7078 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7080 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7083 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7085 vik_layer_emit_update ( VIK_LAYER(vtl) );
7090 // Try again for routes
7091 if (vtl->routes_visible) {
7092 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7094 if ( tp_params.closest_tp ) {
7096 // Always select + highlight the track
7097 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7099 tet->is_waypoint = FALSE;
7101 // Select the Trackpoint
7102 // Can move it immediately when control held or it's the previously selected tp
7103 if ( event->state & GDK_CONTROL_MASK ||
7104 vtl->current_tpl == tp_params.closest_tpl ) {
7105 // Put into 'move buffer'
7106 // NB vvp & vw already set in tet
7107 tet->vtl = (gpointer)vtl;
7108 marker_begin_move (tet, event->x, event->y);
7111 vtl->current_tpl = tp_params.closest_tpl;
7112 vtl->current_tp_id = tp_params.closest_track_id;
7113 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7115 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7118 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7120 vik_layer_emit_update ( VIK_LAYER(vtl) );
7125 /* these aren't the droids you're looking for */
7126 vtl->current_wp = NULL;
7127 vtl->current_wp_id = NULL;
7128 trw_layer_cancel_current_tp ( vtl, FALSE );
7131 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7136 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7138 if ( event->button != 3 )
7141 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7144 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7147 /* Post menu for the currently selected item */
7149 /* See if a track is selected */
7150 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7151 if ( track && track->visible ) {
7153 if ( track->name ) {
7155 if ( vtl->track_right_click_menu )
7156 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
7158 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7165 if ( track->is_route )
7166 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7168 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7170 if ( trkf && udataU.uuid ) {
7173 if ( track->is_route )
7174 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7176 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7178 trw_layer_sublayer_add_menu_items ( vtl,
7179 vtl->track_right_click_menu,
7181 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7187 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7193 /* See if a waypoint is selected */
7194 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7195 if ( waypoint && waypoint->visible ) {
7196 if ( waypoint->name ) {
7198 if ( vtl->wp_right_click_menu )
7199 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
7201 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7204 udata.wp = waypoint;
7207 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7209 if ( wpf && udata.uuid ) {
7210 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7212 trw_layer_sublayer_add_menu_items ( vtl,
7213 vtl->wp_right_click_menu,
7215 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7220 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7229 /* background drawing hook, to be passed the viewport */
7230 static gboolean tool_sync_done = TRUE;
7232 static gboolean tool_sync(gpointer data)
7234 VikViewport *vvp = data;
7235 gdk_threads_enter();
7236 vik_viewport_sync(vvp);
7237 tool_sync_done = TRUE;
7238 gdk_threads_leave();
7242 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7245 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7246 gdk_gc_set_function ( t->gc, GDK_INVERT );
7247 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7248 vik_viewport_sync(t->vvp);
7253 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7255 VikViewport *vvp = t->vvp;
7256 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7257 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7261 if (tool_sync_done) {
7262 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7263 tool_sync_done = FALSE;
7267 static void marker_end_move ( tool_ed_t *t )
7269 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7270 g_object_unref ( t->gc );
7274 /*** Edit waypoint ****/
7276 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7278 tool_ed_t *t = g_new(tool_ed_t, 1);
7284 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7286 WPSearchParams params;
7287 tool_ed_t *t = data;
7288 VikViewport *vvp = t->vvp;
7290 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7297 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7300 if ( vtl->current_wp && vtl->current_wp->visible )
7302 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7304 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7306 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7307 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7309 if ( event->button == 3 )
7310 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7312 marker_begin_move(t, event->x, event->y);
7319 params.x = event->x;
7320 params.y = event->y;
7321 params.closest_wp_id = NULL;
7322 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7323 params.closest_wp = NULL;
7324 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7325 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7327 // how do we get here?
7328 marker_begin_move(t, event->x, event->y);
7329 g_critical("shouldn't be here");
7332 else if ( params.closest_wp )
7334 if ( event->button == 3 )
7335 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7337 vtl->waypoint_rightclick = FALSE;
7339 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7341 vtl->current_wp = params.closest_wp;
7342 vtl->current_wp_id = params.closest_wp_id;
7344 /* could make it so don't update if old WP is off screen and new is null but oh well */
7345 vik_layer_emit_update ( VIK_LAYER(vtl) );
7349 vtl->current_wp = NULL;
7350 vtl->current_wp_id = NULL;
7351 vtl->waypoint_rightclick = FALSE;
7352 vik_layer_emit_update ( VIK_LAYER(vtl) );
7356 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7358 tool_ed_t *t = data;
7359 VikViewport *vvp = t->vvp;
7361 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7366 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7369 if ( event->state & GDK_CONTROL_MASK )
7371 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7373 new_coord = tp->coord;
7377 if ( event->state & GDK_SHIFT_MASK )
7379 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7380 if ( wp && wp != vtl->current_wp )
7381 new_coord = wp->coord;
7386 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7388 marker_moveto ( t, x, y );
7395 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7397 tool_ed_t *t = data;
7398 VikViewport *vvp = t->vvp;
7400 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7403 if ( t->holding && event->button == 1 )
7406 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7409 if ( event->state & GDK_CONTROL_MASK )
7411 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7413 new_coord = tp->coord;
7417 if ( event->state & GDK_SHIFT_MASK )
7419 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7420 if ( wp && wp != vtl->current_wp )
7421 new_coord = wp->coord;
7424 marker_end_move ( t );
7426 vtl->current_wp->coord = new_coord;
7427 vik_layer_emit_update ( VIK_LAYER(vtl) );
7430 /* PUT IN RIGHT PLACE!!! */
7431 if ( event->button == 3 && vtl->waypoint_rightclick )
7433 if ( vtl->wp_right_click_menu )
7434 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7435 if ( vtl->current_wp ) {
7436 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7437 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 );
7438 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7440 vtl->waypoint_rightclick = FALSE;
7445 /*** New track ****/
7447 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7454 GdkDrawable *drawable;
7460 * Draw specified pixmap
7462 static gboolean draw_sync ( gpointer data )
7464 draw_sync_t *ds = (draw_sync_t*) data;
7465 // Sometimes don't want to draw
7466 // normally because another update has taken precedent such as panning the display
7467 // which means this pixmap is no longer valid
7468 if ( ds->vtl->draw_sync_do ) {
7469 gdk_threads_enter();
7470 gdk_draw_drawable (ds->drawable,
7473 0, 0, 0, 0, -1, -1);
7474 ds->vtl->draw_sync_done = TRUE;
7475 gdk_threads_leave();
7480 static gchar* distance_string (gdouble distance)
7484 /* draw label with distance */
7485 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7486 switch (dist_units) {
7487 case VIK_UNITS_DISTANCE_MILES:
7488 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7489 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7490 } else if (distance < 1609.4) {
7491 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7493 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7497 // VIK_UNITS_DISTANCE_KILOMETRES
7498 if (distance >= 1000 && distance < 100000) {
7499 g_sprintf(str, "%3.2f km", distance/1000.0);
7500 } else if (distance < 1000) {
7501 g_sprintf(str, "%d m", (int)distance);
7503 g_sprintf(str, "%d km", (int)distance/1000);
7507 return g_strdup (str);
7511 * Actually set the message in statusbar
7513 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
7515 // Only show elevation data when track has some elevation properties
7516 gchar str_gain_loss[64];
7517 str_gain_loss[0] = '\0';
7518 gchar str_last_step[64];
7519 str_last_step[0] = '\0';
7520 gchar *str_total = distance_string (distance);
7522 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7523 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7524 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7526 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7529 if ( last_step > 0 ) {
7530 gchar *tmp = distance_string (last_step);
7531 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
7535 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
7537 // Write with full gain/loss information
7538 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
7539 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
7541 g_free ( str_total );
7545 * Figure out what information should be set in the statusbar and then write it
7547 static void update_statusbar ( VikTrwLayer *vtl )
7549 // Get elevation data
7550 gdouble elev_gain, elev_loss;
7551 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7553 /* Find out actual distance of current track */
7554 gdouble distance = vik_track_get_length (vtl->current_track);
7556 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
7560 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7562 /* if we haven't sync'ed yet, we don't have time to do more. */
7563 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7564 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7565 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7567 static GdkPixmap *pixmap = NULL;
7569 // Need to check in case window has been resized
7570 w1 = vik_viewport_get_width(vvp);
7571 h1 = vik_viewport_get_height(vvp);
7573 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7575 gdk_drawable_get_size (pixmap, &w2, &h2);
7576 if (w1 != w2 || h1 != h2) {
7577 g_object_unref ( G_OBJECT ( pixmap ) );
7578 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7581 // Reset to background
7582 gdk_draw_drawable (pixmap,
7583 vtl->current_track_newpoint_gc,
7584 vik_viewport_get_pixmap(vvp),
7585 0, 0, 0, 0, -1, -1);
7587 draw_sync_t *passalong;
7590 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7592 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7593 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7594 // thus when we come to reset to the background it would include what we have already drawn!!
7595 gdk_draw_line ( pixmap,
7596 vtl->current_track_newpoint_gc,
7597 x1, y1, event->x, event->y );
7598 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7600 /* Find out actual distance of current track */
7601 gdouble distance = vik_track_get_length (vtl->current_track);
7603 // Now add distance to where the pointer is //
7606 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7607 vik_coord_to_latlon ( &coord, &ll );
7608 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
7609 distance = distance + last_step;
7611 // Get elevation data
7612 gdouble elev_gain, elev_loss;
7613 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7615 // Adjust elevation data (if available) for the current pointer position
7617 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7618 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7619 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7620 // Adjust elevation of last track point
7621 if ( elev_new > last_tpt->altitude )
7623 elev_gain += elev_new - last_tpt->altitude;
7626 elev_loss += last_tpt->altitude - elev_new;
7630 gchar *str = distance_string (distance);
7632 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7633 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
7635 pango_layout_set_text (pl, str, -1);
7637 pango_layout_get_pixel_size ( pl, &wd, &hd );
7640 // offset from cursor a bit depending on font size
7644 // Create a background block to make the text easier to read over the background map
7645 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7646 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7647 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7649 g_object_unref ( G_OBJECT ( pl ) );
7650 g_object_unref ( G_OBJECT ( background_block_gc ) );
7652 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7653 passalong->vtl = vtl;
7654 passalong->pixmap = pixmap;
7655 passalong->drawable = GTK_WIDGET(vvp)->window;
7656 passalong->gc = vtl->current_track_newpoint_gc;
7660 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
7662 // Update statusbar with full gain/loss information
7663 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7667 // draw pixmap when we have time to
7668 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7669 vtl->draw_sync_done = FALSE;
7670 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7672 return VIK_LAYER_TOOL_ACK;
7675 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7677 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7678 vtl->current_track = NULL;
7679 vik_layer_emit_update ( VIK_LAYER(vtl) );
7681 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7683 if ( vtl->current_track->trackpoints )
7685 GList *last = g_list_last(vtl->current_track->trackpoints);
7686 g_free ( last->data );
7687 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7690 update_statusbar ( vtl );
7692 vik_layer_emit_update ( VIK_LAYER(vtl) );
7699 * Common function to handle trackpoint button requests on either a route or a track
7700 * . enables adding a point via normal click
7701 * . enables removal of last point via right click
7702 * . finishing of the track or route via double clicking
7704 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7708 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7711 if ( event->button == 2 ) {
7712 // As the display is panning, the new track pixmap is now invalid so don't draw it
7713 // otherwise this drawing done results in flickering back to an old image
7714 vtl->draw_sync_do = FALSE;
7718 if ( event->button == 3 )
7720 if ( !vtl->current_track )
7723 if ( vtl->current_track->trackpoints )
7725 GList *last = g_list_last(vtl->current_track->trackpoints);
7726 g_free ( last->data );
7727 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7729 update_statusbar ( vtl );
7731 vik_layer_emit_update ( VIK_LAYER(vtl) );
7735 if ( event->type == GDK_2BUTTON_PRESS )
7737 /* subtract last (duplicate from double click) tp then end */
7738 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7740 GList *last = g_list_last(vtl->current_track->trackpoints);
7741 g_free ( last->data );
7742 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7743 /* undo last, then end */
7744 vtl->current_track = NULL;
7746 vik_layer_emit_update ( VIK_LAYER(vtl) );
7750 tp = vik_trackpoint_new();
7751 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
7753 /* snap to other TP */
7754 if ( event->state & GDK_CONTROL_MASK )
7756 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7758 tp->coord = other_tp->coord;
7761 tp->newsegment = FALSE;
7762 tp->has_timestamp = FALSE;
7765 if ( vtl->current_track ) {
7766 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
7767 /* Auto attempt to get elevation from DEM data (if it's available) */
7768 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
7771 vtl->ct_x1 = vtl->ct_x2;
7772 vtl->ct_y1 = vtl->ct_y2;
7773 vtl->ct_x2 = event->x;
7774 vtl->ct_y2 = event->y;
7776 vik_layer_emit_update ( VIK_LAYER(vtl) );
7780 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7782 // ----------------------------------------------------- if current is a route - switch to new track
7783 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
7785 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
7786 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
7788 new_track_create_common ( vtl, name );
7793 return tool_new_track_or_route_click ( vtl, event, vvp );
7796 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7798 if ( event->button == 2 ) {
7799 // Pan moving ended - enable potential point drawing again
7800 vtl->draw_sync_do = TRUE;
7801 vtl->draw_sync_done = TRUE;
7805 /*** New route ****/
7807 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
7812 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7814 // -------------------------- if current is a track - switch to new route
7815 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
7817 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
7818 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
7819 new_route_create_common ( vtl, name );
7823 return tool_new_track_or_route_click ( vtl, event, vvp );
7826 /*** New waypoint ****/
7828 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7833 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7836 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7838 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
7839 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
7840 vik_layer_emit_update ( VIK_LAYER(vtl) );
7845 /*** Edit trackpoint ****/
7847 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
7849 tool_ed_t *t = g_new(tool_ed_t, 1);
7855 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7857 tool_ed_t *t = data;
7858 VikViewport *vvp = t->vvp;
7859 TPSearchParams params;
7860 /* OUTDATED DOCUMENTATION:
7861 find 5 pixel range on each side. then put these UTM, and a pointer
7862 to the winning track name (and maybe the winning track itself), and a
7863 pointer to the winning trackpoint, inside an array or struct. pass
7864 this along, do a foreach on the tracks which will do a foreach on the
7867 params.x = event->x;
7868 params.y = event->y;
7869 params.closest_track_id = NULL;
7870 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7871 params.closest_tp = NULL;
7873 if ( event->button != 1 )
7876 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7879 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
7882 if ( vtl->current_tpl )
7884 /* 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.) */
7885 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7886 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
7891 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
7893 if ( current_tr->visible &&
7894 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7895 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
7896 marker_begin_move ( t, event->x, event->y );
7902 if ( vtl->tracks_visible )
7903 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7905 if ( params.closest_tp )
7907 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
7908 vtl->current_tpl = params.closest_tpl;
7909 vtl->current_tp_id = params.closest_track_id;
7910 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
7911 trw_layer_tpwin_init ( vtl );
7912 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7913 vik_layer_emit_update ( VIK_LAYER(vtl) );
7917 if ( vtl->routes_visible )
7918 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
7920 if ( params.closest_tp )
7922 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
7923 vtl->current_tpl = params.closest_tpl;
7924 vtl->current_tp_id = params.closest_track_id;
7925 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
7926 trw_layer_tpwin_init ( vtl );
7927 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7928 vik_layer_emit_update ( VIK_LAYER(vtl) );
7932 /* these aren't the droids you're looking for */
7936 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7938 tool_ed_t *t = data;
7939 VikViewport *vvp = t->vvp;
7941 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7947 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7950 if ( event->state & GDK_CONTROL_MASK )
7952 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7953 if ( tp && tp != vtl->current_tpl->data )
7954 new_coord = tp->coord;
7956 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7959 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7960 marker_moveto ( t, x, y );
7968 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7970 tool_ed_t *t = data;
7971 VikViewport *vvp = t->vvp;
7973 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7975 if ( event->button != 1)
7980 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7983 if ( event->state & GDK_CONTROL_MASK )
7985 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7986 if ( tp && tp != vtl->current_tpl->data )
7987 new_coord = tp->coord;
7990 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7992 marker_end_move ( t );
7994 /* diff dist is diff from orig */
7996 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7998 vik_layer_emit_update ( VIK_LAYER(vtl) );
8005 #ifdef VIK_CONFIG_GOOGLE
8006 /*** Route Finder ***/
8007 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8012 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8015 if ( !vtl ) return FALSE;
8016 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8017 if ( event->button == 3 && vtl->route_finder_current_track ) {
8019 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8021 vtl->route_finder_coord = *new_end;
8023 vik_layer_emit_update ( VIK_LAYER(vtl) );
8024 /* remove last ' to:...' */
8025 if ( vtl->route_finder_current_track->comment ) {
8026 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8027 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8028 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8029 last_to - vtl->route_finder_current_track->comment - 1);
8030 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8035 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8036 struct LatLon start, end;
8037 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
8038 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
8041 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8042 vik_coord_to_latlon ( &(tmp), &end );
8043 vtl->route_finder_coord = tmp; /* for continuations */
8045 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8046 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8047 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8049 vtl->route_finder_check_added_track = TRUE;
8050 vtl->route_finder_started = FALSE;
8053 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
8054 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
8055 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
8056 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
8057 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
8058 // NB normally this returns a GPX Route - so subsequent usage of it must lookup via the routes hash
8059 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL, NULL );
8062 /* see if anything was done -- a track was added or appended to */
8063 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8064 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 ) );
8065 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8066 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8067 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8068 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8070 vtl->route_finder_added_track = NULL;
8071 vtl->route_finder_check_added_track = FALSE;
8072 vtl->route_finder_append = FALSE;
8074 vik_layer_emit_update ( VIK_LAYER(vtl) );
8076 vtl->route_finder_started = TRUE;
8077 vtl->route_finder_coord = tmp;
8078 vtl->route_finder_current_track = NULL;
8084 /*** Show picture ****/
8086 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8091 /* Params are: vvp, event, last match found or NULL */
8092 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8094 if ( wp->image && wp->visible )
8096 gint x, y, slackx, slacky;
8097 GdkEventButton *event = (GdkEventButton *) params[1];
8099 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8100 slackx = wp->image_width / 2;
8101 slacky = wp->image_height / 2;
8102 if ( x <= event->x + slackx && x >= event->x - slackx
8103 && y <= event->y + slacky && y >= event->y - slacky )
8105 params[2] = wp->image; /* we've found a match. however continue searching
8106 * since we want to find the last match -- that
8107 * is, the match that was drawn last. */
8112 static void trw_layer_show_picture ( gpointer pass_along[6] )
8114 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8116 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8119 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8120 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8121 g_free ( quoted_file );
8122 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8124 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() );
8125 g_error_free ( err );
8128 #endif /* WINDOWS */
8131 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8133 gpointer params[3] = { vvp, event, NULL };
8134 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8136 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8139 static gpointer pass_along[6];
8140 pass_along[0] = vtl;
8141 pass_along[5] = params[2];
8142 trw_layer_show_picture ( pass_along );
8143 return TRUE; /* found a match */
8146 return FALSE; /* go through other layers, searching for a match */
8149 /***************************************************************************
8151 ***************************************************************************/
8154 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8156 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8157 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8160 /* Structure for thumbnail creating data used in the background thread */
8162 VikTrwLayer *vtl; // Layer needed for redrawing
8163 GSList *pics; // Image list
8164 } thumbnail_create_thread_data;
8166 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8168 guint total = g_slist_length(tctd->pics), done = 0;
8169 while ( tctd->pics )
8171 a_thumbnails_create ( (gchar *) tctd->pics->data );
8172 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8174 return -1; /* Abort thread */
8176 tctd->pics = tctd->pics->next;
8179 // Redraw to show the thumbnails as they are now created
8180 if ( IS_VIK_LAYER(tctd->vtl) )
8181 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8186 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8188 while ( tctd->pics )
8190 g_free ( tctd->pics->data );
8191 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8196 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8198 if ( ! vtl->has_verified_thumbnails )
8200 GSList *pics = NULL;
8201 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8204 gint len = g_slist_length ( pics );
8205 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8206 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8209 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8211 (vik_thr_func) create_thumbnails_thread,
8213 (vik_thr_free_func) thumbnail_create_thread_free,
8221 static const gchar* my_track_colors ( gint ii )
8223 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8235 // Fast and reliable way of returning a colour
8236 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8239 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8241 GHashTableIter iter;
8242 gpointer key, value;
8246 g_hash_table_iter_init ( &iter, vtl->tracks );
8248 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8250 // Tracks get a random spread of colours if not already assigned
8251 if ( ! VIK_TRACK(value)->has_color ) {
8252 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8253 VIK_TRACK(value)->color = vtl->track_color;
8255 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8257 VIK_TRACK(value)->has_color = TRUE;
8260 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8263 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8269 g_hash_table_iter_init ( &iter, vtl->routes );
8271 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8273 // Routes get an intermix of reds
8274 if ( ! VIK_TRACK(value)->has_color ) {
8276 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8278 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8279 VIK_TRACK(value)->has_color = TRUE;
8282 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8288 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vp )
8290 trw_layer_verify_thumbnails ( vtl, vp );
8291 trw_layer_track_alloc_colors ( vtl );
8294 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8296 return vtl->coord_mode;
8300 * Uniquify the whole layer
8301 * Also requires the layers panel as the names shown there need updating too
8302 * Returns whether the operation was successful or not
8304 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8307 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8308 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8309 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8315 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8317 vik_coord_convert ( &(wp->coord), *dest_mode );
8320 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
8322 vik_track_convert ( tr, *dest_mode );
8325 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8327 if ( vtl->coord_mode != dest_mode )
8329 vtl->coord_mode = dest_mode;
8330 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8331 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8332 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
8336 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8338 vtl->menu_selection = selection;
8341 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8343 return (vtl->menu_selection);
8346 /* ----------- Downloading maps along tracks --------------- */
8348 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8350 /* TODO: calculating based on current size of viewport */
8351 const gdouble w_at_zoom_0_125 = 0.0013;
8352 const gdouble h_at_zoom_0_125 = 0.0011;
8353 gdouble zoom_factor = zoom_level/0.125;
8355 wh->lat = h_at_zoom_0_125 * zoom_factor;
8356 wh->lon = w_at_zoom_0_125 * zoom_factor;
8358 return 0; /* all OK */
8361 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8363 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8364 (dist->lat >= ABS(to->north_south - from->north_south)))
8367 VikCoord *coord = g_malloc(sizeof(VikCoord));
8368 coord->mode = VIK_COORD_LATLON;
8370 if (ABS(gradient) < 1) {
8371 if (from->east_west > to->east_west)
8372 coord->east_west = from->east_west - dist->lon;
8374 coord->east_west = from->east_west + dist->lon;
8375 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8377 if (from->north_south > to->north_south)
8378 coord->north_south = from->north_south - dist->lat;
8380 coord->north_south = from->north_south + dist->lat;
8381 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8387 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8389 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8390 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8392 VikCoord *next = from;
8394 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8396 list = g_list_prepend(list, next);
8402 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8404 typedef struct _Rect {
8409 #define GLRECT(iter) ((Rect *)((iter)->data))
8412 GList *rects_to_download = NULL;
8415 if (get_download_area_width(vvp, zoom_level, &wh))
8418 GList *iter = tr->trackpoints;
8422 gboolean new_map = TRUE;
8423 VikCoord *cur_coord, tl, br;
8426 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8428 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8429 rect = g_malloc(sizeof(Rect));
8432 rect->center = *cur_coord;
8433 rects_to_download = g_list_prepend(rects_to_download, rect);
8438 gboolean found = FALSE;
8439 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8440 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8451 GList *fillins = NULL;
8452 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8453 /* seems that ATM the function get_next_coord works only for LATLON */
8454 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8455 /* fill-ins for far apart points */
8456 GList *cur_rect, *next_rect;
8457 for (cur_rect = rects_to_download;
8458 (next_rect = cur_rect->next) != NULL;
8459 cur_rect = cur_rect->next) {
8460 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8461 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8462 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8466 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8469 GList *iter = fillins;
8471 cur_coord = (VikCoord *)(iter->data);
8472 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8473 rect = g_malloc(sizeof(Rect));
8476 rect->center = *cur_coord;
8477 rects_to_download = g_list_prepend(rects_to_download, rect);
8482 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8483 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8487 for (iter = fillins; iter; iter = iter->next)
8489 g_list_free(fillins);
8491 if (rects_to_download) {
8492 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8493 g_free(rect_iter->data);
8494 g_list_free(rects_to_download);
8498 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8501 gint selected_map, default_map;
8502 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8503 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8504 gint selected_zoom, default_zoom;
8508 VikTrwLayer *vtl = pass_along[0];
8509 VikLayersPanel *vlp = pass_along[1];
8511 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8512 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8514 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8518 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8520 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8521 int num_maps = g_list_length(vmls);
8524 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8528 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8529 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8531 gchar **np = map_names;
8532 VikMapsLayer **lp = map_layers;
8533 for (i = 0; i < num_maps; i++) {
8534 gboolean dup = FALSE;
8535 vml = (VikMapsLayer *)(vmls->data);
8536 for (j = 0; j < i; j++) { /* no duplicate allowed */
8537 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8544 *np++ = vik_maps_layer_get_map_label(vml);
8550 num_maps = lp - map_layers;
8552 for (default_map = 0; default_map < num_maps; default_map++) {
8553 /* TODO: check for parent layer's visibility */
8554 if (VIK_LAYER(map_layers[default_map])->visible)
8557 default_map = (default_map == num_maps) ? 0 : default_map;
8559 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8560 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8561 if (cur_zoom == zoom_vals[default_zoom])
8564 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8566 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8569 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8572 for (i = 0; i < num_maps; i++)
8573 g_free(map_names[i]);
8581 /**** lowest waypoint number calculation ***/
8582 static gint highest_wp_number_name_to_number(const gchar *name) {
8583 if ( strlen(name) == 3 ) {
8585 if ( n < 100 && name[0] != '0' )
8587 if ( n < 10 && name[0] != '0' )
8595 static void highest_wp_number_reset(VikTrwLayer *vtl)
8597 vtl->highest_wp_number = -1;
8600 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8602 /* if is bigger that top, add it */
8603 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8604 if ( new_wp_num > vtl->highest_wp_number )
8605 vtl->highest_wp_number = new_wp_num;
8608 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8610 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8611 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8612 if ( vtl->highest_wp_number == old_wp_num ) {
8614 vtl->highest_wp_number--;
8616 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8617 /* search down until we find something that *does* exist */
8619 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8620 vtl->highest_wp_number--;
8621 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8626 /* get lowest unused number */
8627 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8630 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8632 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8633 return g_strdup(buf);