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 if ( vp ) 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 if ( vp ) 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 if ( vp ) 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->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1166 rv->wp_draw_symbols = TRUE;
1167 rv->wp_font_size = FS_MEDIUM;
1168 rv->track_draw_speed_factor = 30.0;
1169 rv->line_thickness = 1;
1171 rv->draw_sync_done = TRUE;
1172 rv->draw_sync_do = TRUE;
1174 rv->image_cache = g_queue_new();
1175 rv->image_size = 64;
1176 rv->image_alpha = 255;
1177 rv->image_cache_size = 300;
1178 rv->drawimages = TRUE;
1179 rv->drawlabels = TRUE;
1180 // Everything else is 0, FALSE or NULL
1186 static void trw_layer_free ( VikTrwLayer *trwlayer )
1188 g_hash_table_destroy(trwlayer->waypoints);
1189 g_hash_table_destroy(trwlayer->tracks);
1191 /* ODC: replace with GArray */
1192 trw_layer_free_track_gcs ( trwlayer );
1194 if ( trwlayer->wp_right_click_menu )
1195 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1197 if ( trwlayer->track_right_click_menu )
1198 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1200 if ( trwlayer->wplabellayout != NULL)
1201 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1203 if ( trwlayer->waypoint_gc != NULL )
1204 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1206 if ( trwlayer->waypoint_text_gc != NULL )
1207 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1209 if ( trwlayer->waypoint_bg_gc != NULL )
1210 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1212 if ( trwlayer->tpwin != NULL )
1213 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1215 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1216 g_queue_free ( trwlayer->image_cache );
1219 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1223 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1224 dp->xmpp = vik_viewport_get_xmpp ( vp );
1225 dp->ympp = vik_viewport_get_ympp ( vp );
1226 dp->width = vik_viewport_get_width ( vp );
1227 dp->height = vik_viewport_get_height ( vp );
1228 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1229 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1231 dp->center = vik_viewport_get_center ( vp );
1232 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1233 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1238 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1239 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1240 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1242 dp->ce1 = dp->center->east_west-w2;
1243 dp->ce2 = dp->center->east_west+w2;
1244 dp->cn1 = dp->center->north_south-h2;
1245 dp->cn2 = dp->center->north_south+h2;
1246 } else if ( dp->lat_lon ) {
1247 VikCoord upperleft, bottomright;
1248 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1249 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1250 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1251 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1252 dp->ce1 = upperleft.east_west;
1253 dp->ce2 = bottomright.east_west;
1254 dp->cn1 = bottomright.north_south;
1255 dp->cn2 = upperleft.north_south;
1260 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1261 * Here a simple traffic like light colour system is used:
1262 * . slow points are red
1263 * . average is yellow
1264 * . fast points are green
1266 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1269 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1270 if ( average_speed > 0 ) {
1271 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1272 if ( rv < low_speed )
1273 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1274 else if ( rv > high_speed )
1275 return VIK_TRW_LAYER_TRACK_GC_FAST;
1277 return VIK_TRW_LAYER_TRACK_GC_AVER;
1280 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1283 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1285 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1286 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1287 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1288 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1291 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1293 /* TODO: this function is a mess, get rid of any redundancy */
1294 GList *list = track->trackpoints;
1296 gboolean useoldvals = TRUE;
1298 gboolean drawpoints;
1300 gboolean drawelevation;
1301 gdouble min_alt, max_alt, alt_diff = 0;
1303 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1304 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1307 if ( dp->vtl->drawelevation )
1309 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1310 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1311 alt_diff = max_alt - min_alt;
1314 if ( ! track->visible )
1317 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1318 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1319 trw_layer_draw_track ( id, track, dp, TRUE );
1321 if ( draw_track_outline )
1322 drawpoints = drawstops = FALSE;
1324 drawpoints = dp->vtl->drawpoints;
1325 drawstops = dp->vtl->drawstops;
1328 gboolean drawing_highlight = FALSE;
1329 /* Current track - used for creation */
1330 if ( track == dp->vtl->current_track )
1331 main_gc = dp->vtl->current_track_gc;
1333 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1334 /* Draw all tracks of the layer in special colour */
1335 /* if track is member of selected layer or is the current selected track
1336 then draw in the highlight colour.
1337 NB this supercedes the drawmode */
1338 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1339 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1340 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1341 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1342 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1343 drawing_highlight = TRUE;
1346 if ( !drawing_highlight ) {
1347 // Still need to figure out the gc according to the drawing mode:
1348 switch ( dp->vtl->drawmode ) {
1349 case DRAWMODE_BY_TRACK:
1350 if ( dp->vtl->track_1color_gc )
1351 g_object_unref ( dp->vtl->track_1color_gc );
1352 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1353 main_gc = dp->vtl->track_1color_gc;
1356 // Mostly for DRAWMODE_ALL_SAME_COLOR
1357 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1358 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1365 int x, y, oldx, oldy;
1366 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1368 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1370 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1372 // Draw the first point as something a bit different from the normal points
1373 // ATM it's slightly bigger and a triangle
1375 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1376 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1382 gdouble average_speed = 0.0;
1383 gdouble low_speed = 0.0;
1384 gdouble high_speed = 0.0;
1385 // If necessary calculate these values - which is done only once per track redraw
1386 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1387 // the percentage factor away from the average speed determines transistions between the levels
1388 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1389 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1390 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1393 while ((list = g_list_next(list)))
1395 tp = VIK_TRACKPOINT(list->data);
1396 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1398 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1399 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1400 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1401 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1402 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1404 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1407 * If points are the same in display coordinates, don't draw.
1409 if ( useoldvals && x == oldx && y == oldy )
1411 // Still need to process points to ensure 'stops' are drawn if required
1412 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1413 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1414 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 );
1419 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1420 if ( drawpoints || dp->vtl->drawlines ) {
1421 // setup main_gc for both point and line drawing
1422 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1423 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 ) );
1427 if ( drawpoints && ! draw_track_outline )
1432 * The concept of drawing stops is that a trackpoint
1433 * that is if the next trackpoint has a timestamp far into
1434 * the future, we draw a circle of 6x trackpoint size,
1435 * instead of a rectangle of 2x trackpoint size.
1436 * This is drawn first so the trackpoint will be drawn on top
1439 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1440 /* Stop point. Draw 6x circle. Always in redish colour */
1441 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 );
1443 /* Regular point - draw 2x square. */
1444 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1447 /* Final point - draw 4x circle. */
1448 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 );
1451 if ((!tp->newsegment) && (dp->vtl->drawlines))
1454 /* UTM only: zone check */
1455 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1456 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1459 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1461 if ( draw_track_outline ) {
1462 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1466 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1468 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1470 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1475 tmp[1].y = oldy-FIXALTITUDE(list->data);
1477 tmp[2].y = y-FIXALTITUDE(list->next->data);
1482 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1483 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1485 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1486 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1488 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1493 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1494 // Draw an arrow at the mid point to show the direction of the track
1495 // Code is a rework from vikwindow::draw_ruler()
1496 gint midx = (oldx + x) / 2;
1497 gint midy = (oldy + y) / 2;
1499 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1500 // Avoid divide by zero and ensure at least 1 pixel big
1502 gdouble dx = (oldx - midx) / len;
1503 gdouble dy = (oldy - midy) / len;
1504 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1505 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1515 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1517 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1518 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1520 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1522 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1523 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 ));
1527 * If points are the same in display coordinates, don't draw.
1529 if ( x != oldx || y != oldy )
1531 if ( draw_track_outline )
1532 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1534 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1540 * If points are the same in display coordinates, don't draw.
1542 if ( x != oldx && y != oldy )
1544 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1545 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1555 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1556 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1558 trw_layer_draw_track ( id, track, dp, FALSE );
1561 static void cached_pixbuf_free ( CachedPixbuf *cp )
1563 g_object_unref ( G_OBJECT(cp->pixbuf) );
1564 g_free ( cp->image );
1567 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1569 return strcmp ( cp->image, name );
1572 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1575 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1576 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1577 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1580 GdkPixbuf *sym = NULL;
1581 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1583 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1585 if ( wp->image && dp->vtl->drawimages )
1587 GdkPixbuf *pixbuf = NULL;
1590 if ( dp->vtl->image_alpha == 0)
1593 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1595 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1598 gchar *image = wp->image;
1599 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1600 if ( ! regularthumb )
1602 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1603 image = "\x12\x00"; /* this shouldn't occur naturally. */
1607 CachedPixbuf *cp = NULL;
1608 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1609 if ( dp->vtl->image_size == 128 )
1610 cp->pixbuf = regularthumb;
1613 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1614 g_assert ( cp->pixbuf );
1615 g_object_unref ( G_OBJECT(regularthumb) );
1617 cp->image = g_strdup ( image );
1619 /* needed so 'click picture' tool knows how big the pic is; we don't
1620 * store it in cp because they may have been freed already. */
1621 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1622 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1624 g_queue_push_head ( dp->vtl->image_cache, cp );
1625 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1626 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1628 pixbuf = cp->pixbuf;
1632 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1638 w = gdk_pixbuf_get_width ( pixbuf );
1639 h = gdk_pixbuf_get_height ( pixbuf );
1641 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1643 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1644 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1645 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1646 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
1647 // Highlighted - so draw a little border around the chosen one
1648 // single line seems a little weak so draw 2 of them
1649 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1650 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1651 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1652 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1655 if ( dp->vtl->image_alpha == 255 )
1656 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1658 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1660 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1664 /* DRAW ACTUAL DOT */
1665 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1666 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 );
1668 else if ( wp == dp->vtl->current_wp ) {
1669 switch ( dp->vtl->wp_symbol ) {
1670 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;
1671 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;
1672 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;
1673 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 );
1674 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 );
1678 switch ( dp->vtl->wp_symbol ) {
1679 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;
1680 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;
1681 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;
1682 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 );
1683 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;
1687 if ( dp->vtl->drawlabels )
1689 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1690 gint label_x, label_y;
1692 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1694 // Could this stored in the waypoint rather than recreating each pass?
1695 gchar *fsize = NULL;
1696 switch (dp->vtl->wp_font_size) {
1697 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1698 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1699 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1700 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1701 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1702 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1703 default: fsize = g_strdup ( "medium" ); break;
1706 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1708 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1709 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1711 // Fallback if parse failure
1712 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1714 g_free ( wp_label_markup );
1717 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1718 label_x = x - width/2;
1720 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1722 label_y = y - dp->vtl->wp_size - height - 2;
1724 /* if highlight mode on, then draw background text in highlight colour */
1725 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1726 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1727 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1728 wp == vik_window_get_selected_waypoint ( dp->vw ) )
1729 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1731 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1734 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1736 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1741 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1743 static struct DrawingParams dp;
1744 g_assert ( l != NULL );
1746 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1748 if ( l->tracks_visible )
1749 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1751 if ( l->routes_visible )
1752 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1754 if (l->waypoints_visible)
1755 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1758 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1761 if ( vtl->track_bg_gc )
1763 g_object_unref ( vtl->track_bg_gc );
1764 vtl->track_bg_gc = NULL;
1766 if ( vtl->track_1color_gc )
1768 g_object_unref ( vtl->track_1color_gc );
1769 vtl->track_1color_gc = NULL;
1771 if ( vtl->current_track_gc )
1773 g_object_unref ( vtl->current_track_gc );
1774 vtl->current_track_gc = NULL;
1776 if ( vtl->current_track_newpoint_gc )
1778 g_object_unref ( vtl->current_track_newpoint_gc );
1779 vtl->current_track_newpoint_gc = NULL;
1782 if ( ! vtl->track_gc )
1784 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1785 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1786 g_array_free ( vtl->track_gc, TRUE );
1787 vtl->track_gc = NULL;
1790 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1792 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1793 gint width = vtl->line_thickness;
1795 if ( vtl->track_gc )
1796 trw_layer_free_track_gcs ( vtl );
1798 if ( vtl->track_bg_gc )
1799 g_object_unref ( vtl->track_bg_gc );
1800 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1802 // Ensure new track drawing heeds line thickness setting
1803 // however always have a minium of 2, as 1 pixel is really narrow
1804 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1806 if ( vtl->current_track_gc )
1807 g_object_unref ( vtl->current_track_gc );
1808 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1809 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1811 // 'newpoint' gc is exactly the same as the current track gc
1812 if ( vtl->current_track_newpoint_gc )
1813 g_object_unref ( vtl->current_track_newpoint_gc );
1814 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1815 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1817 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1819 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1820 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1822 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1823 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1824 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1826 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
1828 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1831 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1833 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1834 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1836 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1837 /* early exit, as the rest is GUI related */
1841 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1842 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1844 gdk_color_parse ( "#000000", &(rv->track_color) ); // Black
1846 trw_layer_new_track_gcs ( rv, vp );
1848 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1849 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1850 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1851 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1853 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1855 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1860 #define SMALL_ICON_SIZE 18
1862 * Can accept a null symbol, and may return null value
1864 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1866 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1867 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1868 // So needing a small icon for the treeview may need some resizing:
1869 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1870 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1874 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1876 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1878 GdkPixbuf *pixbuf = NULL;
1880 if ( track->has_color ) {
1881 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1882 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1883 // Here is some magic found to do the conversion
1884 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1885 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1886 ((track->color.green & 0xff00) << 8) |
1887 (track->color.blue & 0xff00);
1889 gdk_pixbuf_fill ( pixbuf, pixel );
1892 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1893 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 );
1895 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 );
1899 g_object_unref (pixbuf);
1901 *new_iter = *((GtkTreeIter *) pass_along[1]);
1902 if ( track->is_route )
1903 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1905 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1907 if ( ! track->visible )
1908 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1911 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1913 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1915 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1916 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 );
1918 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 );
1921 *new_iter = *((GtkTreeIter *) pass_along[1]);
1922 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1924 if ( ! wp->visible )
1925 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1928 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1930 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1931 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1933 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1937 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1939 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1940 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1942 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1946 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1948 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1949 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1951 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1955 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1958 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1960 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1961 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1962 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1964 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1967 if ( g_hash_table_size (vtl->routes) > 0 ) {
1969 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
1971 pass_along[0] = &(vtl->routes_iter);
1972 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
1974 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
1976 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
1979 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
1980 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
1982 pass_along[0] = &(vtl->waypoints_iter);
1983 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1985 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1987 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1992 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1996 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1997 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1998 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
1999 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2001 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2003 return (t->visible ^= 1);
2007 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2009 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2011 return (t->visible ^= 1);
2015 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2017 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2019 return (t->visible ^= 1);
2028 * Return a property about tracks for this layer
2030 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2032 return vtl->line_thickness;
2035 // Structure to hold multiple track information for a layer
2044 * Build up layer multiple track information via updating the tooltip_tracks structure
2046 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2048 tt->length = tt->length + vik_track_get_length (tr);
2050 // Ensure times are available
2051 if ( tr->trackpoints &&
2052 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2053 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2056 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2057 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2059 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2060 // Hence initialize to the first 'proper' value
2061 if ( tt->start_time == 0 )
2062 tt->start_time = t1;
2063 if ( tt->end_time == 0 )
2066 // Update find the earliest / last times
2067 if ( t1 < tt->start_time )
2068 tt->start_time = t1;
2069 if ( t2 > tt->end_time )
2072 // Keep track of total time
2073 // there maybe gaps within a track (eg segments)
2074 // but this should be generally good enough for a simple indicator
2075 tt->duration = tt->duration + (int)(t2-t1);
2080 * Generate tooltip text for the layer.
2081 * This is relatively complicated as it considers information for
2082 * no tracks, a single track or multiple tracks
2083 * (which may or may not have timing information)
2085 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2096 static gchar tmp_buf[128];
2099 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2101 // Safety check - I think these should always be valid
2102 if ( vtl->tracks && vtl->waypoints ) {
2103 tooltip_tracks tt = { 0.0, 0, 0 };
2104 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2106 GDate* gdate_start = g_date_new ();
2107 g_date_set_time_t (gdate_start, tt.start_time);
2109 GDate* gdate_end = g_date_new ();
2110 g_date_set_time_t (gdate_end, tt.end_time);
2112 if ( g_date_compare (gdate_start, gdate_end) ) {
2113 // Dates differ so print range on separate line
2114 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2115 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2116 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2119 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2120 if ( tt.start_time != 0 )
2121 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2125 if ( tt.length > 0.0 ) {
2126 gdouble len_in_units;
2128 // Setup info dependent on distance units
2129 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2130 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2131 len_in_units = VIK_METERS_TO_MILES(tt.length);
2134 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2135 len_in_units = tt.length/1000.0;
2138 // Timing information if available
2140 if ( tt.duration > 0 ) {
2141 g_snprintf (tbuf1, sizeof(tbuf1),
2142 _(" in %d:%02d hrs:mins"),
2143 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2145 g_snprintf (tbuf2, sizeof(tbuf2),
2146 _("\n%sTotal Length %.1f %s%s"),
2147 tbuf3, len_in_units, tbuf4, tbuf1);
2150 // Put together all the elements to form compact tooltip text
2151 g_snprintf (tmp_buf, sizeof(tmp_buf),
2152 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2153 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2155 g_date_free (gdate_start);
2156 g_date_free (gdate_end);
2163 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2167 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2169 // Very simple tooltip - may expand detail in the future...
2170 static gchar tmp_buf[32];
2171 g_snprintf (tmp_buf, sizeof(tmp_buf),
2173 g_hash_table_size (l->tracks));
2177 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2179 // Very simple tooltip - may expand detail in the future...
2180 static gchar tmp_buf[32];
2181 g_snprintf (tmp_buf, sizeof(tmp_buf),
2183 g_hash_table_size (l->routes));
2188 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2189 // Same tooltip for a route
2190 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2193 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2194 tr = g_hash_table_lookup ( l->tracks, sublayer );
2196 tr = g_hash_table_lookup ( l->routes, sublayer );
2199 // Could be a better way of handling strings - but this works...
2200 gchar time_buf1[20];
2201 gchar time_buf2[20];
2202 time_buf1[0] = '\0';
2203 time_buf2[0] = '\0';
2204 static gchar tmp_buf[100];
2205 // Compact info: Short date eg (11/20/99), duration and length
2206 // Hopefully these are the things that are most useful and so promoted into the tooltip
2207 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2208 // %x The preferred date representation for the current locale without the time.
2209 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2210 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2211 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2213 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2216 // Get length and consider the appropriate distance units
2217 gdouble tr_len = vik_track_get_length(tr);
2218 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2219 switch (dist_units) {
2220 case VIK_UNITS_DISTANCE_KILOMETRES:
2221 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2223 case VIK_UNITS_DISTANCE_MILES:
2224 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2233 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2235 // Very simple tooltip - may expand detail in the future...
2236 static gchar tmp_buf[32];
2237 g_snprintf (tmp_buf, sizeof(tmp_buf),
2239 g_hash_table_size (l->waypoints));
2243 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2245 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2246 // NB It's OK to return NULL
2257 * Function to show basic track point information on the statusbar
2259 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2262 switch (a_vik_get_units_height ()) {
2263 case VIK_UNITS_HEIGHT_FEET:
2264 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2267 //VIK_UNITS_HEIGHT_METRES:
2268 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2273 if ( trkpt->has_timestamp ) {
2274 // Compact date time format
2275 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2279 // Position is put later on, as this bit may not be seen if the display is not big enough,
2280 // one can easily use the current pointer position to see this if needed
2281 gchar *lat = NULL, *lon = NULL;
2282 static struct LatLon ll;
2283 vik_coord_to_latlon (&(trkpt->coord), &ll);
2284 a_coords_latlon_to_string ( &ll, &lat, &lon );
2287 // Again is put later on, as this bit may not be seen if the display is not big enough
2288 // trackname can be seen from the treeview (when enabled)
2289 // Also name could be very long to not leave room for anything else
2292 if ( vtl->current_tp_track ) {
2293 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2296 // Combine parts to make overall message
2297 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2298 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2305 * Function to show basic waypoint information on the statusbar
2307 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2310 switch (a_vik_get_units_height ()) {
2311 case VIK_UNITS_HEIGHT_FEET:
2312 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2315 //VIK_UNITS_HEIGHT_METRES:
2316 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2320 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2321 // one can easily use the current pointer position to see this if needed
2322 gchar *lat = NULL, *lon = NULL;
2323 static struct LatLon ll;
2324 vik_coord_to_latlon (&(wpt->coord), &ll);
2325 a_coords_latlon_to_string ( &ll, &lat, &lon );
2327 // Combine parts to make overall message
2330 // Add comment if available
2331 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2333 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2334 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2341 * General layer selection function, find out which bit is selected and take appropriate action
2343 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2346 l->current_wp = NULL;
2347 l->current_wp_id = NULL;
2348 trw_layer_cancel_current_tp ( l, FALSE );
2351 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2355 case VIK_TREEVIEW_TYPE_LAYER:
2357 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2358 /* Mark for redraw */
2363 case VIK_TREEVIEW_TYPE_SUBLAYER:
2367 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2369 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2370 /* Mark for redraw */
2374 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2376 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2377 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2378 /* Mark for redraw */
2382 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2384 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2385 /* Mark for redraw */
2389 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2391 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2392 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2393 /* Mark for redraw */
2397 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2399 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2400 /* Mark for redraw */
2404 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2406 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2408 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2409 // Show some waypoint info
2410 set_statusbar_msg_info_wpt ( l, wpt );
2411 /* Mark for redraw */
2418 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2427 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2432 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2437 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2442 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2444 return l->waypoints;
2448 * ATM use a case sensitive find
2449 * Finds the first one
2451 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2453 if ( wp && wp->name )
2454 if ( ! strcmp ( wp->name, name ) )
2460 * Get waypoint by name - not guaranteed to be unique
2461 * Finds the first one
2463 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2465 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2469 * ATM use a case sensitive find
2470 * Finds the first one
2472 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2474 if ( trk && trk->name )
2475 if ( ! strcmp ( trk->name, name ) )
2481 * Get track by name - not guaranteed to be unique
2482 * Finds the first one
2484 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2486 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2490 * Get route by name - not guaranteed to be unique
2491 * Finds the first one
2493 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2495 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2498 static void trw_layer_find_maxmin_waypoints ( const gpointer id, const VikWaypoint *w, struct LatLon maxmin[2] )
2500 static VikCoord fixme;
2501 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2502 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2503 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2504 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2505 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2506 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2507 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2508 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2509 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2512 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2514 GList *tr = trk->trackpoints;
2515 static VikCoord fixme;
2519 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2520 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2521 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2522 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2523 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2524 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2525 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2526 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2527 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2532 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2534 // Continually reuse maxmin to find the latest maximum and minimum values
2535 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2536 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2537 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2540 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2542 /* 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... */
2543 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2544 trw_layer_find_maxmin (vtl, maxmin);
2545 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2549 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2550 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2555 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2558 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2559 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2561 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2564 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2566 /* First set the center [in case previously viewing from elsewhere] */
2567 /* Then loop through zoom levels until provided positions are in view */
2568 /* This method is not particularly fast - but should work well enough */
2569 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2571 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2572 vik_viewport_set_center_coord ( vvp, &coord );
2574 /* Convert into definite 'smallest' and 'largest' positions */
2575 struct LatLon minmin;
2576 if ( maxmin[0].lat < maxmin[1].lat )
2577 minmin.lat = maxmin[0].lat;
2579 minmin.lat = maxmin[1].lat;
2581 struct LatLon maxmax;
2582 if ( maxmin[0].lon > maxmin[1].lon )
2583 maxmax.lon = maxmin[0].lon;
2585 maxmax.lon = maxmin[1].lon;
2587 /* Never zoom in too far - generally not that useful, as too close ! */
2588 /* Always recalculate the 'best' zoom level */
2590 vik_viewport_set_zoom ( vvp, zoom );
2592 gdouble min_lat, max_lat, min_lon, max_lon;
2593 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2594 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2595 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2596 /* NB I think the logic used in this test to determine if the bounds is within view
2597 fails if track goes across 180 degrees longitude.
2598 Hopefully that situation is not too common...
2599 Mind you viking doesn't really do edge locations to well anyway */
2600 if ( min_lat < minmin.lat &&
2601 max_lat > minmin.lat &&
2602 min_lon < maxmax.lon &&
2603 max_lon > maxmax.lon )
2604 /* Found within zoom level */
2609 vik_viewport_set_zoom ( vvp, zoom );
2613 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2615 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2616 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2617 trw_layer_find_maxmin (vtl, maxmin);
2618 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2621 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2626 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2628 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])) ) ) {
2629 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2632 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2635 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2637 GtkWidget *file_selector;
2639 gboolean failed = FALSE;
2640 file_selector = gtk_file_chooser_dialog_new (title,
2642 GTK_FILE_CHOOSER_ACTION_SAVE,
2643 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2644 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2646 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2648 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2650 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2651 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2653 gtk_widget_hide ( file_selector );
2654 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2659 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2661 gtk_widget_hide ( file_selector );
2662 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2667 gtk_widget_destroy ( file_selector );
2669 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2672 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2674 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2677 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2679 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2682 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2684 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2685 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2686 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2687 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2689 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2691 g_free ( auto_save_name );
2694 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2696 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2697 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2698 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2699 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2701 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2703 g_free ( auto_save_name );
2707 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2710 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2712 gchar *name_used = NULL;
2715 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2716 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2718 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2722 gchar *quoted_file = g_shell_quote ( name_used );
2723 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2724 g_free ( quoted_file );
2725 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2727 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2728 g_error_free ( err );
2732 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2733 //g_remove ( name_used );
2734 // Perhaps should be deleted when the program ends?
2735 // For now leave it to the user to delete it / use system temp cleanup methods.
2736 g_free ( name_used );
2740 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2742 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2745 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2747 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2750 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2752 gpointer layer_and_vlp[2];
2753 layer_and_vlp[0] = pass_along[0];
2754 layer_and_vlp[1] = pass_along[1];
2756 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2758 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2759 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2761 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2763 if ( !trk || !trk->name )
2766 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2767 gchar *auto_save_name = g_strdup ( trk->name );
2768 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2769 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2771 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2773 g_free ( auto_save_name );
2777 VikWaypoint *wp; // input
2778 gpointer uuid; // output
2781 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2783 wpu_udata *user_data = udata;
2784 if ( wp == user_data->wp ) {
2785 user_data->uuid = id;
2791 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2793 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2794 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2795 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2797 GTK_RESPONSE_REJECT,
2799 GTK_RESPONSE_ACCEPT,
2802 GtkWidget *label, *entry;
2803 label = gtk_label_new(_("Waypoint Name:"));
2804 entry = gtk_entry_new();
2806 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2807 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2808 gtk_widget_show_all ( label );
2809 gtk_widget_show_all ( entry );
2811 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2813 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2815 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2816 // Find *first* wp with the given name
2817 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2820 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2823 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2824 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2826 // Find and select on the side panel
2831 // Hmmm, want key of it
2832 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2834 if ( wpf && udata.uuid ) {
2835 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2836 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2845 gtk_widget_destroy ( dia );
2848 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2850 gchar *default_name = highest_wp_number_get(vtl);
2851 VikWaypoint *wp = vik_waypoint_new();
2852 gchar *returned_name;
2854 wp->coord = *def_coord;
2856 // Attempt to auto set height if DEM data is available
2857 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2858 if ( elev != VIK_DEM_INVALID_ELEVATION )
2859 wp->altitude = (gdouble)elev;
2861 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2863 if ( returned_name )
2866 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2867 g_free (default_name);
2868 g_free (returned_name);
2871 g_free (default_name);
2872 vik_waypoint_free(wp);
2876 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2878 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2879 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2880 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2881 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2882 VikViewport *vvp = vik_window_viewport(vw);
2884 // Note the order is max part first then min part - thus reverse order of use in min_max function:
2885 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
2886 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2887 vik_layers_panel_emit_update ( vlp );
2890 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2892 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2893 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2894 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2896 trw_layer_find_maxmin (vtl, maxmin);
2897 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2898 vik_layers_panel_emit_update ( vlp );
2901 #ifdef VIK_CONFIG_GEOTAG
2902 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2904 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2906 // Update directly - not changing the mtime
2907 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2910 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2912 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2915 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2919 * Use code in separate file for this feature as reasonably complex
2921 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2923 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2924 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2925 // Unset so can be reverified later if necessary
2926 vtl->has_verified_thumbnails = FALSE;
2928 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2934 static void trw_layer_geotagging ( gpointer lav[2] )
2936 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2937 // Unset so can be reverified later if necessary
2938 vtl->has_verified_thumbnails = FALSE;
2940 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2947 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2950 * Acquire into this TRW Layer straight from GPS Device
2952 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2954 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2955 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2956 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2957 VikViewport *vvp = vik_window_viewport(vw);
2959 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2960 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2963 #ifdef VIK_CONFIG_GOOGLE
2965 * Acquire into this TRW Layer from Google Directions
2967 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2969 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2970 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2971 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2972 VikViewport *vvp = vik_window_viewport(vw);
2974 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2978 #ifdef VIK_CONFIG_OPENSTREETMAP
2980 * Acquire into this TRW Layer from OSM
2982 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2984 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2985 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2986 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2987 VikViewport *vvp = vik_window_viewport(vw);
2989 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2993 * Acquire into this TRW Layer from OSM for 'My' Traces
2995 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
2997 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2998 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2999 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3000 VikViewport *vvp = vik_window_viewport(vw);
3002 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface );
3006 #ifdef VIK_CONFIG_GEOCACHES
3008 * Acquire into this TRW Layer from Geocaching.com
3010 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3012 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3013 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3014 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3015 VikViewport *vvp = vik_window_viewport(vw);
3017 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
3021 #ifdef VIK_CONFIG_GEOTAG
3023 * Acquire into this TRW Layer from images
3025 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3027 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3028 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3029 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3030 VikViewport *vvp = vik_window_viewport(vw);
3032 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3033 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
3035 // Reverify thumbnails as they may have changed
3036 vtl->has_verified_thumbnails = FALSE;
3037 trw_layer_verify_thumbnails ( vtl, NULL );
3041 static void trw_layer_gps_upload ( gpointer lav[2] )
3043 gpointer pass_along[6];
3044 pass_along[0] = lav[0];
3045 pass_along[1] = lav[1];
3046 pass_along[2] = NULL; // No track - operate on the layer
3047 pass_along[3] = NULL;
3048 pass_along[4] = NULL;
3049 pass_along[5] = NULL;
3051 trw_layer_gps_upload_any ( pass_along );
3055 * If pass_along[3] is defined that this will upload just that track
3057 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3059 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3060 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3062 // May not actually get a track here as pass_along[2&3] can be null
3063 VikTrack *track = NULL;
3064 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3065 gboolean xfer_all = FALSE;
3067 if ( pass_along[2] ) {
3069 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3070 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3073 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3074 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3077 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3080 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3084 else if ( !pass_along[4] )
3085 xfer_all = TRUE; // i.e. whole layer
3087 if (track && !track->visible) {
3088 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3092 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3093 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3094 GTK_DIALOG_DESTROY_WITH_PARENT,
3096 GTK_RESPONSE_ACCEPT,
3098 GTK_RESPONSE_REJECT,
3101 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3102 GtkWidget *response_w = NULL;
3103 #if GTK_CHECK_VERSION (2, 20, 0)
3104 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3108 gtk_widget_grab_focus ( response_w );
3110 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3112 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3113 datasource_gps_clean_up ( dgs );
3114 gtk_widget_destroy ( dialog );
3118 // Get info from reused datasource dialog widgets
3119 gchar* protocol = datasource_gps_get_protocol ( dgs );
3120 gchar* port = datasource_gps_get_descriptor ( dgs );
3121 // NB don't free the above strings as they're references to values held elsewhere
3122 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3123 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3124 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3125 gboolean turn_off = datasource_gps_get_off ( dgs );
3127 gtk_widget_destroy ( dialog );
3129 // When called from the viewport - work the corresponding layerspanel:
3131 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3134 // Apply settings to transfer to the GPS device
3141 vik_layers_panel_get_viewport (vlp),
3150 * Acquire into this TRW Layer from any GPS Babel supported file
3152 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3154 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3155 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3156 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3157 VikViewport *vvp = vik_window_viewport(vw);
3159 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3162 static void trw_layer_new_wp ( gpointer lav[2] )
3164 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3165 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3166 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3167 instead return true if you want to update. */
3168 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 )
3169 vik_layers_panel_emit_update ( vlp );
3172 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3174 vtl->current_track = vik_track_new();
3175 vtl->current_track->visible = TRUE;
3176 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3177 // Create track with the preferred colour from the layer properties
3178 vtl->current_track->color = vtl->track_color;
3180 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3181 vtl->current_track->has_color = TRUE;
3182 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3185 static void trw_layer_new_track ( gpointer lav[2] )
3187 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3189 if ( ! vtl->current_track ) {
3190 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3191 new_track_create_common ( vtl, name );
3193 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3197 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3199 vtl->current_track = vik_track_new();
3200 vtl->current_track->visible = TRUE;
3201 vtl->current_track->is_route = TRUE;
3202 // By default make all routes red
3203 vtl->current_track->has_color = TRUE;
3204 gdk_color_parse ( "red", &vtl->current_track->color );
3205 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3208 static void trw_layer_new_route ( gpointer lav[2] )
3210 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3212 if ( ! vtl->current_track ) {
3213 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3214 new_route_create_common ( vtl, name );
3215 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3219 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3221 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3222 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3224 if ( g_hash_table_size (vtl->routes) > 0 ) {
3225 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3226 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3227 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3228 vik_layers_panel_emit_update ( vlp );
3233 static void trw_layer_finish_track ( gpointer lav[2] )
3235 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3236 vtl->current_track = NULL;
3237 vik_layer_emit_update ( VIK_LAYER(vtl) );
3240 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3242 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3243 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3245 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3246 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3247 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3248 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3249 vik_layers_panel_emit_update ( vlp );
3253 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3255 /* NB do not care if wp is visible or not */
3256 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3259 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3261 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3262 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3264 /* Only 1 waypoint - jump straight to it */
3265 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3266 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3267 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3269 /* If at least 2 waypoints - find center and then zoom to fit */
3270 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3272 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3273 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3274 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3277 vik_layers_panel_emit_update ( vlp );
3280 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3282 static gpointer pass_along[2];
3284 GtkWidget *export_submenu;
3285 pass_along[0] = vtl;
3286 pass_along[1] = vlp;
3288 item = gtk_menu_item_new();
3289 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3290 gtk_widget_show ( item );
3292 if ( vtl->current_track ) {
3293 if ( vtl->current_track->is_route )
3294 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3296 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3297 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3298 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3299 gtk_widget_show ( item );
3302 item = gtk_menu_item_new ();
3303 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3304 gtk_widget_show ( item );
3307 /* Now with icons */
3308 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3309 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3311 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3312 gtk_widget_show ( item );
3314 GtkWidget *view_submenu = gtk_menu_new();
3315 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3316 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3317 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3318 gtk_widget_show ( item );
3319 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3321 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3323 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3324 gtk_widget_show ( item );
3326 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3328 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3329 gtk_widget_show ( item );
3331 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3332 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3333 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3334 gtk_widget_show ( item );
3336 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3337 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3338 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3339 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3340 gtk_widget_show ( item );
3342 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3343 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3344 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3345 gtk_widget_show ( item );
3347 export_submenu = gtk_menu_new ();
3348 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3349 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3350 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3351 gtk_widget_show ( item );
3352 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3354 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3355 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3356 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3357 gtk_widget_show ( item );
3359 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3360 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3361 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3362 gtk_widget_show ( item );
3364 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3365 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3366 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3367 gtk_widget_show ( item );
3369 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3370 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3371 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3372 gtk_widget_show ( item );
3374 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3375 item = gtk_menu_item_new_with_mnemonic ( external1 );
3376 g_free ( external1 );
3377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3378 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3379 gtk_widget_show ( item );
3381 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3382 item = gtk_menu_item_new_with_mnemonic ( external2 );
3383 g_free ( external2 );
3384 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3385 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3386 gtk_widget_show ( item );
3388 GtkWidget *new_submenu = gtk_menu_new();
3389 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3390 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3391 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3392 gtk_widget_show(item);
3393 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3395 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3396 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3397 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3398 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3399 gtk_widget_show ( item );
3401 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3402 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3403 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3404 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3405 gtk_widget_show ( item );
3406 // Make it available only when a new track *not* already in progress
3407 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3409 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3410 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3412 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3413 gtk_widget_show ( item );
3414 // Make it available only when a new track *not* already in progress
3415 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3417 #ifdef VIK_CONFIG_GEOTAG
3418 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3420 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3421 gtk_widget_show ( item );
3424 GtkWidget *acquire_submenu = gtk_menu_new ();
3425 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3426 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3427 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3428 gtk_widget_show ( item );
3429 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3431 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3432 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3433 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3434 gtk_widget_show ( item );
3436 #ifdef VIK_CONFIG_GOOGLE
3437 item = gtk_menu_item_new_with_mnemonic ( _("From Google _Directions...") );
3438 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3439 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3440 gtk_widget_show ( item );
3443 #ifdef VIK_CONFIG_OPENSTREETMAP
3444 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3445 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3446 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3447 gtk_widget_show ( item );
3449 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3450 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3451 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3452 gtk_widget_show ( item );
3455 #ifdef VIK_CONFIG_GEONAMES
3456 GtkWidget *wikipedia_submenu = gtk_menu_new();
3457 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3458 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3459 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3460 gtk_widget_show(item);
3461 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3463 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3464 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3465 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3466 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3467 gtk_widget_show ( item );
3469 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3470 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3471 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3472 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3473 gtk_widget_show ( item );
3476 #ifdef VIK_CONFIG_GEOCACHES
3477 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3478 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3479 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3480 gtk_widget_show ( item );
3483 #ifdef VIK_CONFIG_GEOTAG
3484 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3485 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3486 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3487 gtk_widget_show ( item );
3490 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3491 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3492 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3493 gtk_widget_show ( item );
3495 GtkWidget *upload_submenu = gtk_menu_new ();
3496 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3497 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3498 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3499 gtk_widget_show ( item );
3500 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3502 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3503 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3505 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3506 gtk_widget_show ( item );
3508 #ifdef VIK_CONFIG_OPENSTREETMAP
3509 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3510 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3511 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3512 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3513 gtk_widget_show ( item );
3516 GtkWidget *delete_submenu = gtk_menu_new ();
3517 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3518 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3519 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3520 gtk_widget_show ( item );
3521 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3523 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3524 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3525 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3526 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3527 gtk_widget_show ( item );
3529 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3530 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3531 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3532 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3533 gtk_widget_show ( item );
3535 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3536 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3537 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3538 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3539 gtk_widget_show ( item );
3541 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3542 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3543 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3544 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3545 gtk_widget_show ( item );
3547 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3548 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3550 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3551 gtk_widget_show ( item );
3553 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3554 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3555 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3556 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3557 gtk_widget_show ( item );
3559 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3560 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3562 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3563 gtk_widget_show ( item );
3566 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3567 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3569 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3570 gtk_widget_show ( item );
3574 // Fake Waypoint UUIDs vi simple increasing integer
3575 static guint wp_uuid = 0;
3577 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3581 vik_waypoint_set_name (wp, name);
3583 if ( VIK_LAYER(vtl)->realized )
3585 // Do we need to create the sublayer:
3586 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3587 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3590 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3592 // Visibility column always needed for waypoints
3593 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3594 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 );
3596 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 );
3598 // Actual setting of visibility dependent on the waypoint
3599 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3601 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3604 highest_wp_number_add_wp(vtl, name);
3605 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3609 // Fake Track UUIDs vi simple increasing integer
3610 static guint tr_uuid = 0;
3612 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3616 vik_track_set_name (t, name);
3618 if ( VIK_LAYER(vtl)->realized )
3620 // Do we need to create the sublayer:
3621 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3622 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3625 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3626 // Visibility column always needed for tracks
3627 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3628 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 );
3630 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 );
3632 // Actual setting of visibility dependent on the track
3633 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3635 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3638 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3640 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3643 // Fake Route UUIDs vi simple increasing integer
3644 static guint rt_uuid = 0;
3646 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3650 vik_track_set_name (t, name);
3652 if ( VIK_LAYER(vtl)->realized )
3654 // Do we need to create the sublayer:
3655 if ( g_hash_table_size (vtl->routes) == 0 ) {
3656 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3659 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3660 // Visibility column always needed for tracks
3661 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3662 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 );
3664 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 );
3666 // Actual setting of visibility dependent on the track
3667 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3669 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3672 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3674 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3677 /* to be called whenever a track has been deleted or may have been changed. */
3678 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3680 if (vtl->current_tp_track == trk )
3681 trw_layer_cancel_current_tp ( vtl, FALSE );
3684 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3687 gchar *newname = g_strdup(name);
3692 switch ( sublayer_type ) {
3693 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3694 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3696 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3697 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3700 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3703 // If found a name already in use try adding 1 to it and we try again
3705 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3707 newname = new_newname;
3710 } while ( id != NULL);
3715 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3717 // No more uniqueness of name forced when loading from a file
3718 // This now makes this function a little redunant as we just flow the parameters through
3719 vik_trw_layer_add_waypoint ( vtl, name, wp );
3722 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3724 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3725 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3726 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3727 vik_track_free ( tr );
3728 vtl->route_finder_append = FALSE; /* this means we have added it */
3731 // No more uniqueness of name forced when loading from a file
3733 vik_trw_layer_add_route ( vtl, name, tr );
3735 vik_trw_layer_add_track ( vtl, name, tr );
3737 if ( vtl->route_finder_check_added_track ) {
3738 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3739 vtl->route_finder_added_track = tr;
3744 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3746 *l = g_list_append(*l, id);
3750 * Move an item from one TRW layer to another TRW layer
3752 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3754 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3755 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3757 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3759 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3760 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3761 vik_trw_layer_delete_track ( vtl_src, trk );
3764 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3765 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3767 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3769 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3770 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3771 vik_trw_layer_delete_route ( vtl_src, trk );
3774 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3775 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3777 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3779 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3780 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3781 trw_layer_delete_waypoint ( vtl_src, wp );
3785 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3787 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3788 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3790 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3791 GList *items = NULL;
3794 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3795 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3797 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3798 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3800 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3801 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3806 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3807 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3809 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3810 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3812 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3819 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3820 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3825 VikTrack *trk; // input
3826 gpointer uuid; // output
3829 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3831 trku_udata *user_data = udata;
3832 if ( trk == user_data->trk ) {
3833 user_data->uuid = id;
3839 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3841 gboolean was_visible = FALSE;
3843 if ( trk && trk->name ) {
3845 if ( trk == vtl->current_track ) {
3846 vtl->current_track = NULL;
3847 vtl->current_tp_track = NULL;
3848 vtl->current_tp_id = NULL;
3849 vtl->moving_tp = FALSE;
3852 was_visible = trk->visible;
3854 if ( trk == vtl->route_finder_current_track )
3855 vtl->route_finder_current_track = NULL;
3857 if ( trk == vtl->route_finder_added_track )
3858 vtl->route_finder_added_track = NULL;
3864 // Hmmm, want key of it
3865 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3867 if ( trkf && udata.uuid ) {
3868 /* could be current_tp, so we have to check */
3869 trw_layer_cancel_tps_of_track ( vtl, trk );
3871 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3874 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3875 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3876 g_hash_table_remove ( vtl->tracks, udata.uuid );
3878 // If last sublayer, then remove sublayer container
3879 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3880 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3888 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
3890 gboolean was_visible = FALSE;
3892 if ( trk && trk->name ) {
3894 if ( trk == vtl->current_track ) {
3895 vtl->current_track = NULL;
3896 vtl->current_tp_track = NULL;
3897 vtl->current_tp_id = NULL;
3898 vtl->moving_tp = FALSE;
3901 was_visible = trk->visible;
3903 if ( trk == vtl->route_finder_current_track )
3904 vtl->route_finder_current_track = NULL;
3906 if ( trk == vtl->route_finder_added_track )
3907 vtl->route_finder_added_track = NULL;
3913 // Hmmm, want key of it
3914 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
3916 if ( trkf && udata.uuid ) {
3917 /* could be current_tp, so we have to check */
3918 trw_layer_cancel_tps_of_track ( vtl, trk );
3920 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
3923 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3924 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
3925 g_hash_table_remove ( vtl->routes, udata.uuid );
3927 // If last sublayer, then remove sublayer container
3928 if ( g_hash_table_size (vtl->routes) == 0 ) {
3929 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
3937 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3939 gboolean was_visible = FALSE;
3941 if ( wp && wp->name ) {
3943 if ( wp == vtl->current_wp ) {
3944 vtl->current_wp = NULL;
3945 vtl->current_wp_id = NULL;
3946 vtl->moving_wp = FALSE;
3949 was_visible = wp->visible;
3955 // Hmmm, want key of it
3956 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3958 if ( wpf && udata.uuid ) {
3959 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3962 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3963 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3965 highest_wp_number_remove_wp(vtl, wp->name);
3966 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3968 // If last sublayer, then remove sublayer container
3969 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3970 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3980 // Only for temporary use by trw_layer_delete_waypoint_by_name
3981 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3983 wpu_udata *user_data = udata;
3984 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3985 user_data->uuid = id;
3992 * Delete a waypoint by the given name
3993 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3994 * as there be multiple waypoints with the same name
3996 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3999 // Fake a waypoint with the given name
4000 udata.wp = vik_waypoint_new ();
4001 vik_waypoint_set_name (udata.wp, name);
4002 // Currently only the name is used in this waypoint find function
4005 // Hmmm, want key of it
4006 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4008 vik_waypoint_free (udata.wp);
4010 if ( wpf && udata.uuid )
4011 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4017 VikTrack *trk; // input
4018 gpointer uuid; // output
4021 // Only for temporary use by trw_layer_delete_track_by_name
4022 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4024 tpu_udata *user_data = udata;
4025 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4026 user_data->uuid = id;
4033 * Delete a track by the given name
4034 * NOTE: ATM this will delete the first encountered Track with the specified name
4035 * as there may be multiple tracks with the same name within the specified hash table
4037 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4040 // Fake a track with the given name
4041 udata.trk = vik_track_new ();
4042 vik_track_set_name (udata.trk, name);
4043 // Currently only the name is used in this waypoint find function
4046 // Hmmm, want key of it
4047 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4049 vik_track_free (udata.trk);
4051 if ( trkf && udata.uuid ) {
4052 // This could be a little better written...
4053 if ( vtl->tracks == ht_tracks )
4054 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4055 if ( vtl->routes == ht_tracks )
4056 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4063 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4065 vik_treeview_item_delete (vt, it );
4068 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4071 vtl->current_track = NULL;
4072 vtl->route_finder_current_track = NULL;
4073 vtl->route_finder_added_track = NULL;
4074 if (vtl->current_tp_track)
4075 trw_layer_cancel_current_tp(vtl, FALSE);
4077 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4078 g_hash_table_remove_all(vtl->routes_iters);
4079 g_hash_table_remove_all(vtl->routes);
4081 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4083 vik_layer_emit_update ( VIK_LAYER(vtl) );
4086 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4089 vtl->current_track = NULL;
4090 vtl->route_finder_current_track = NULL;
4091 vtl->route_finder_added_track = NULL;
4092 if (vtl->current_tp_track)
4093 trw_layer_cancel_current_tp(vtl, FALSE);
4095 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4096 g_hash_table_remove_all(vtl->tracks_iters);
4097 g_hash_table_remove_all(vtl->tracks);
4099 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4101 vik_layer_emit_update ( VIK_LAYER(vtl) );
4104 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4106 vtl->current_wp = NULL;
4107 vtl->current_wp_id = NULL;
4108 vtl->moving_wp = FALSE;
4110 highest_wp_number_reset(vtl);
4112 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4113 g_hash_table_remove_all(vtl->waypoints_iters);
4114 g_hash_table_remove_all(vtl->waypoints);
4116 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4118 vik_layer_emit_update ( VIK_LAYER(vtl) );
4121 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4123 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4124 // Get confirmation from the user
4125 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4126 _("Are you sure you want to delete all tracks in %s?"),
4127 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4128 vik_trw_layer_delete_all_tracks (vtl);
4131 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4133 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4134 // Get confirmation from the user
4135 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4136 _("Are you sure you want to delete all routes in %s?"),
4137 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4138 vik_trw_layer_delete_all_routes (vtl);
4141 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4143 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4144 // Get confirmation from the user
4145 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4146 _("Are you sure you want to delete all waypoints in %s?"),
4147 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4148 vik_trw_layer_delete_all_waypoints (vtl);
4151 static void trw_layer_delete_item ( gpointer pass_along[6] )
4153 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4154 gboolean was_visible = FALSE;
4155 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4157 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4158 if ( wp && wp->name ) {
4159 if ( GPOINTER_TO_INT ( pass_along[4]) )
4160 // Get confirmation from the user
4161 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4162 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4163 _("Are you sure you want to delete the waypoint \"%s\""),
4166 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4169 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4171 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4172 if ( trk && trk->name ) {
4173 if ( GPOINTER_TO_INT ( pass_along[4]) )
4174 // Get confirmation from the user
4175 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4176 _("Are you sure you want to delete the track \"%s\""),
4179 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4184 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4185 if ( trk && trk->name ) {
4186 if ( GPOINTER_TO_INT ( pass_along[4]) )
4187 // Get confirmation from the user
4188 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4189 _("Are you sure you want to delete the route \"%s\""),
4192 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4196 vik_layer_emit_update ( VIK_LAYER(vtl) );
4200 static void trw_layer_properties_item ( gpointer pass_along[7] )
4202 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4203 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4205 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4207 if ( wp && wp->name )
4209 gboolean updated = FALSE;
4210 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
4212 if ( updated && wp->symbol && pass_along[6] )
4213 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4215 if ( updated && VIK_LAYER(vtl)->visible )
4216 vik_layer_emit_update ( VIK_LAYER(vtl) );
4222 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4223 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4225 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4227 if ( tr && tr->name )
4229 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4232 pass_along[1], /* vlp */
4233 pass_along[5], /* vvp */
4234 pass_along[6]); /* iter */
4240 * Update the treeview of the track id - primarily to update the icon
4242 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4248 gpointer *trkf = NULL;
4249 if ( trk->is_route )
4250 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4252 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4254 if ( trkf && udata.uuid ) {
4256 GtkTreeIter *iter = NULL;
4257 if ( trk->is_route )
4258 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4260 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4263 // TODO: Make this a function
4264 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4265 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4266 ((trk->color.green & 0xff00) << 8) |
4267 (trk->color.blue & 0xff00);
4268 gdk_pixbuf_fill ( pixbuf, pixel );
4269 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4270 g_object_unref (pixbuf);
4277 Parameter 1 -> VikLayersPanel
4278 Parameter 2 -> VikLayer
4279 Parameter 3 -> VikViewport
4281 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4284 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4285 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4288 /* since vlp not set, vl & vvp should be valid instead! */
4290 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4291 vik_layer_emit_update ( VIK_LAYER(vl) );
4296 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4298 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4300 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4301 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4303 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4305 if ( track && track->trackpoints )
4306 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4309 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4311 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4313 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4314 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4316 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4318 if ( track && track->trackpoints )
4320 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4322 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4323 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4324 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4325 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4326 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4330 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4332 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4334 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4335 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4337 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4342 // Converting a track to a route can be a bit more complicated,
4343 // so give a chance to change our minds:
4344 if ( !trk->is_route &&
4345 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4346 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4348 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4349 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4354 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4357 trk_copy->is_route = !trk_copy->is_route;
4359 // ATM can't set name to self - so must create temporary copy
4360 gchar *name = g_strdup ( trk_copy->name );
4362 // Delete old one and then add new one
4363 if ( trk->is_route ) {
4364 vik_trw_layer_delete_route ( vtl, trk );
4365 vik_trw_layer_add_track ( vtl, name, trk_copy );
4368 // Extra route conversion bits...
4369 vik_track_merge_segments ( trk_copy );
4370 vik_track_to_routepoints ( trk_copy );
4372 vik_trw_layer_delete_track ( vtl, trk );
4373 vik_trw_layer_add_route ( vtl, name, trk_copy );
4377 // Update in case color of track / route changes when moving between sublayers
4378 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4382 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4384 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4386 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4387 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4389 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4394 vtl->current_track = track;
4395 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);
4397 if ( track->trackpoints )
4398 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4401 #ifdef VIK_CONFIG_GOOGLE
4403 * extend a track using route finder
4405 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4407 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4408 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4411 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4413 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4414 vtl->route_finder_coord = last_coord;
4415 vtl->route_finder_current_track = track;
4416 vtl->route_finder_started = TRUE;
4418 if ( track->trackpoints )
4419 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4424 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4426 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4427 /* Also warn if overwrite old elevation data */
4428 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4430 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4431 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4433 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4436 vik_track_apply_dem_data ( track );
4439 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4441 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4443 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4444 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4446 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4451 GList *trps = track->trackpoints;
4454 trps = g_list_last(trps);
4455 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4458 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4460 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4462 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4463 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4465 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4470 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4473 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4476 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4478 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4480 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4481 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4483 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4488 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4491 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4494 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4496 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4498 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4499 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4501 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4506 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4509 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4513 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4515 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4517 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4519 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4520 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4522 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4524 if ( trk && trk->trackpoints )
4526 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4527 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4528 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4529 if ( pass_along[1] )
4530 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4532 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4536 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4538 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4539 trw_layer_tpwin_init ( vtl );
4542 /*************************************
4543 * merge/split by time routines
4544 *************************************/
4546 /* called for each key in track hash table.
4547 * If the current track has the same time stamp type, add it to the result,
4548 * except the one pointed by "exclude".
4549 * set exclude to NULL if there is no exclude to check.
4550 * Note that the result is in reverse (for performance reasons).
4555 gboolean with_timestamps;
4557 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4559 twt_udata *user_data = udata;
4560 VikTrackpoint *p1, *p2;
4562 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4566 if (VIK_TRACK(value)->trackpoints) {
4567 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4568 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4570 if ( user_data->with_timestamps ) {
4571 if (!p1->has_timestamp || !p2->has_timestamp) {
4576 // Don't add tracks with timestamps when getting non timestamp tracks
4577 if (p1->has_timestamp || p2->has_timestamp) {
4583 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4586 /* called for each key in track hash table. if original track user_data[1] is close enough
4587 * to the passed one, add it to list in user_data[0]
4589 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4592 VikTrackpoint *p1, *p2;
4593 VikTrack *trk = VIK_TRACK(value);
4595 GList **nearby_tracks = ((gpointer *)user_data)[0];
4596 GList *tpoints = ((gpointer *)user_data)[1];
4599 * detect reasons for not merging, and return
4600 * if no reason is found not to merge, then do it.
4603 // Exclude the original track from the compiled list
4604 if (trk->trackpoints == tpoints) {
4608 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4609 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4611 if (trk->trackpoints) {
4612 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4613 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4615 if (!p1->has_timestamp || !p2->has_timestamp) {
4616 //g_print("no timestamp\n");
4620 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4621 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4622 if (! (abs(t1 - p2->timestamp) < threshold ||
4624 abs(p1->timestamp - t2) < threshold)
4631 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4634 /* comparison function used to sort tracks; a and b are hash table keys */
4635 /* Not actively used - can be restored if needed
4636 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4638 GHashTable *tracks = user_data;
4641 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4642 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4644 if (t1 < t2) return -1;
4645 if (t1 > t2) return 1;
4650 /* comparison function used to sort trackpoints */
4651 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4653 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4655 if (t1 < t2) return -1;
4656 if (t1 > t2) return 1;
4661 * comparison function which can be used to sort tracks or waypoints by name
4663 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4665 const gchar* namea = (const gchar*) a;
4666 const gchar* nameb = (const gchar*) b;
4667 if ( namea == NULL || nameb == NULL)
4670 // Same sort method as used in the vik_treeview_*_alphabetize functions
4671 return strcmp ( namea, nameb );
4675 * Attempt to merge selected track with other tracks specified by the user
4676 * Tracks to merge with must be of the same 'type' as the selected track -
4677 * either all with timestamps, or all without timestamps
4679 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4681 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4682 GList *other_tracks = NULL;
4683 GHashTable *ght_tracks;
4684 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4685 ght_tracks = vtl->routes;
4687 ght_tracks = vtl->tracks;
4689 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4694 if ( !track->trackpoints )
4698 udata.result = &other_tracks;
4699 udata.exclude = track->trackpoints;
4700 // Allow merging with 'similar' time type time tracks
4701 // i.e. either those times, or those without
4702 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4704 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4705 other_tracks = g_list_reverse(other_tracks);
4707 if ( !other_tracks ) {
4708 if ( udata.with_timestamps )
4709 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4711 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4715 // Sort alphabetically for user presentation
4716 // Convert into list of names for usage with dialog function
4717 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4718 GList *other_tracks_names = NULL;
4719 GList *iter = g_list_first ( other_tracks );
4721 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4722 iter = g_list_next ( iter );
4725 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4727 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4731 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4732 g_list_free(other_tracks);
4733 g_list_free(other_tracks_names);
4738 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4739 VikTrack *merge_track;
4740 if ( track->is_route )
4741 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4743 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4746 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4747 merge_track->trackpoints = NULL;
4748 if ( track->is_route )
4749 vik_trw_layer_delete_route (vtl, merge_track);
4751 vik_trw_layer_delete_track (vtl, merge_track);
4752 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4755 /* TODO: free data before free merge_list */
4756 for (l = merge_list; l != NULL; l = g_list_next(l))
4758 g_list_free(merge_list);
4759 vik_layer_emit_update( VIK_LAYER(vtl) );
4763 // c.f. trw_layer_sorted_track_id_by_name_list
4764 // but don't add the specified track to the list (normally current track)
4765 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4767 twt_udata *user_data = udata;
4770 if (trk->trackpoints == user_data->exclude) {
4774 // Sort named list alphabetically
4775 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4779 * Join - this allows combining 'tracks' and 'track routes'
4780 * i.e. doesn't care about whether tracks have consistent timestamps
4781 * ATM can only append one track at a time to the currently selected track
4783 static void trw_layer_append_track ( gpointer pass_along[6] )
4786 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4788 GHashTable *ght_tracks;
4789 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4790 ght_tracks = vtl->routes;
4792 ght_tracks = vtl->tracks;
4794 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4799 GList *other_tracks_names = NULL;
4801 // Sort alphabetically for user presentation
4802 // Convert into list of names for usage with dialog function
4803 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4805 udata.result = &other_tracks_names;
4806 udata.exclude = trk->trackpoints;
4808 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4810 // Note the limit to selecting one track only
4811 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4812 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4813 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4816 trk->is_route ? _("Append Route"): _("Append Track"),
4817 trk->is_route ? _("Select the route to append after the current route") :
4818 _("Select the track to append after the current track") );
4820 g_list_free(other_tracks_names);
4822 // It's a list, but shouldn't contain more than one other track!
4823 if ( append_list ) {
4825 for (l = append_list; l != NULL; l = g_list_next(l)) {
4826 // TODO: at present this uses the first track found by name,
4827 // which with potential multiple same named tracks may not be the one selected...
4828 VikTrack *append_track;
4829 if ( trk->is_route )
4830 append_track = vik_trw_layer_get_route ( vtl, l->data );
4832 append_track = vik_trw_layer_get_track ( vtl, l->data );
4834 if ( append_track ) {
4835 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4836 append_track->trackpoints = NULL;
4837 if ( trk->is_route )
4838 vik_trw_layer_delete_route (vtl, append_track);
4840 vik_trw_layer_delete_track (vtl, append_track);
4843 for (l = append_list; l != NULL; l = g_list_next(l))
4845 g_list_free(append_list);
4846 vik_layer_emit_update( VIK_LAYER(vtl) );
4851 * Very similar to trw_layer_append_track for joining
4852 * but this allows selection from the 'other' list
4853 * If a track is selected, then is shows routes and joins the selected one
4854 * If a route is selected, then is shows tracks and joins the selected one
4856 static void trw_layer_append_other ( gpointer pass_along[6] )
4859 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4861 GHashTable *ght_mykind, *ght_others;
4862 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4863 ght_mykind = vtl->routes;
4864 ght_others = vtl->tracks;
4867 ght_mykind = vtl->tracks;
4868 ght_others = vtl->routes;
4871 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
4876 GList *other_tracks_names = NULL;
4878 // Sort alphabetically for user presentation
4879 // Convert into list of names for usage with dialog function
4880 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4882 udata.result = &other_tracks_names;
4883 udata.exclude = trk->trackpoints;
4885 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4887 // Note the limit to selecting one track only
4888 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4889 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4890 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4893 trk->is_route ? _("Append Track"): _("Append Route"),
4894 trk->is_route ? _("Select the track to append after the current route") :
4895 _("Select the route to append after the current track") );
4897 g_list_free(other_tracks_names);
4899 // It's a list, but shouldn't contain more than one other track!
4900 if ( append_list ) {
4902 for (l = append_list; l != NULL; l = g_list_next(l)) {
4903 // TODO: at present this uses the first track found by name,
4904 // which with potential multiple same named tracks may not be the one selected...
4906 // Get FROM THE OTHER TYPE list
4907 VikTrack *append_track;
4908 if ( trk->is_route )
4909 append_track = vik_trw_layer_get_track ( vtl, l->data );
4911 append_track = vik_trw_layer_get_route ( vtl, l->data );
4913 if ( append_track ) {
4915 if ( !append_track->is_route &&
4916 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
4917 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
4919 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4920 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
4921 vik_track_merge_segments ( append_track );
4922 vik_track_to_routepoints ( append_track );
4929 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4930 append_track->trackpoints = NULL;
4932 // Delete copied which is FROM THE OTHER TYPE list
4933 if ( trk->is_route )
4934 vik_trw_layer_delete_track (vtl, append_track);
4936 vik_trw_layer_delete_route (vtl, append_track);
4939 for (l = append_list; l != NULL; l = g_list_next(l))
4941 g_list_free(append_list);
4942 vik_layer_emit_update( VIK_LAYER(vtl) );
4946 /* merge by segments */
4947 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4949 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4950 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4951 guint segments = vik_track_merge_segments ( trk );
4952 // NB currently no need to redraw as segments not actually shown on the display
4953 // However inform the user of what happened:
4955 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4956 g_snprintf(str, 64, tmp_str, segments);
4957 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4960 /* merge by time routine */
4961 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4963 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4967 GList *tracks_with_timestamp = NULL;
4968 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4969 if (orig_trk->trackpoints &&
4970 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4971 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4976 udata.result = &tracks_with_timestamp;
4977 udata.exclude = orig_trk->trackpoints;
4978 udata.with_timestamps = TRUE;
4979 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4980 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4982 if (!tracks_with_timestamp) {
4983 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4986 g_list_free(tracks_with_timestamp);
4988 static guint threshold_in_minutes = 1;
4989 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4990 _("Merge Threshold..."),
4991 _("Merge when time between tracks less than:"),
4992 &threshold_in_minutes)) {
4996 // keep attempting to merge all tracks until no merges within the time specified is possible
4997 gboolean attempt_merge = TRUE;
4998 GList *nearby_tracks = NULL;
5000 static gpointer params[3];
5002 while ( attempt_merge ) {
5004 // Don't try again unless tracks have changed
5005 attempt_merge = FALSE;
5007 trps = orig_trk->trackpoints;
5011 if (nearby_tracks) {
5012 g_list_free(nearby_tracks);
5013 nearby_tracks = NULL;
5016 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5017 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5019 /* g_print("Original track times: %d and %d\n", t1, t2); */
5020 params[0] = &nearby_tracks;
5021 params[1] = (gpointer)trps;
5022 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5024 /* get a list of adjacent-in-time tracks */
5025 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5028 GList *l = nearby_tracks;
5031 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5032 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5034 t1 = get_first_trackpoint(l)->timestamp;
5035 t2 = get_last_trackpoint(l)->timestamp;
5036 #undef get_first_trackpoint
5037 #undef get_last_trackpoint
5038 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5041 /* remove trackpoints from merged track, delete track */
5042 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
5043 VIK_TRACK(l->data)->trackpoints = NULL;
5044 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5046 // Tracks have changed, therefore retry again against all the remaining tracks
5047 attempt_merge = TRUE;
5052 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5055 g_list_free(nearby_tracks);
5056 vik_layer_emit_update( VIK_LAYER(vtl) );
5060 * Split a track at the currently selected trackpoint
5062 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5064 if ( !vtl->current_tpl )
5067 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5068 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5070 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5071 GList *newglist = g_list_alloc ();
5072 newglist->prev = NULL;
5073 newglist->next = vtl->current_tpl->next;
5074 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5075 tr->trackpoints = newglist;
5077 vtl->current_tpl->next->prev = newglist; /* end old track here */
5078 vtl->current_tpl->next = NULL;
5080 vtl->current_tpl = newglist; /* change tp to first of new track. */
5081 vtl->current_tp_track = tr;
5084 vik_trw_layer_add_route ( vtl, name, tr );
5086 vik_trw_layer_add_track ( vtl, name, tr );
5092 // Also need id of newly created track
5095 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5097 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5099 if ( trkf && udata.uuid )
5100 vtl->current_tp_id = udata.uuid;
5102 vtl->current_tp_id = NULL;
5104 vik_layer_emit_update(VIK_LAYER(vtl));
5109 /* split by time routine */
5110 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5112 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5113 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5114 GList *trps = track->trackpoints;
5116 GList *newlists = NULL;
5117 GList *newtps = NULL;
5118 static guint thr = 1;
5125 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5126 _("Split Threshold..."),
5127 _("Split when time between trackpoints exceeds:"),
5132 /* iterate through trackpoints, and copy them into new lists without touching original list */
5133 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5137 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5139 g_print("panic: ts < prev_ts: this should never happen!\n");
5142 if (ts - prev_ts > thr*60) {
5143 /* flush accumulated trackpoints into new list */
5144 newlists = g_list_append(newlists, g_list_reverse(newtps));
5148 /* accumulate trackpoint copies in newtps, in reverse order */
5149 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5151 iter = g_list_next(iter);
5154 newlists = g_list_append(newlists, g_list_reverse(newtps));
5157 /* put lists of trackpoints into tracks */
5159 // Only bother updating if the split results in new tracks
5160 if (g_list_length (newlists) > 1) {
5165 tr = vik_track_copy ( track, FALSE );
5166 tr->trackpoints = (GList *)(iter->data);
5168 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5169 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5170 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5171 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5173 iter = g_list_next(iter);
5175 // Remove original track and then update the display
5176 vik_trw_layer_delete_track (vtl, track);
5177 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5179 g_list_free(newlists);
5183 * Split a track by the number of points as specified by the user
5185 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5187 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5189 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5190 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5192 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5197 // Check valid track
5198 GList *trps = track->trackpoints;
5202 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5203 _("Split Every Nth Point"),
5204 _("Split on every Nth point:"),
5205 250, // Default value as per typical limited track capacity of various GPS devices
5209 // Was a valid number returned?
5215 GList *newlists = NULL;
5216 GList *newtps = NULL;
5221 /* accumulate trackpoint copies in newtps, in reverse order */
5222 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5224 if (count >= points) {
5225 /* flush accumulated trackpoints into new list */
5226 newlists = g_list_append(newlists, g_list_reverse(newtps));
5230 iter = g_list_next(iter);
5233 // If there is a remaining chunk put that into the new split list
5234 // This may well be the whole track if no split points were encountered
5236 newlists = g_list_append(newlists, g_list_reverse(newtps));
5239 /* put lists of trackpoints into tracks */
5241 // Only bother updating if the split results in new tracks
5242 if (g_list_length (newlists) > 1) {
5247 tr = vik_track_copy ( track, FALSE );
5248 tr->trackpoints = (GList *)(iter->data);
5250 if ( track->is_route ) {
5251 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5252 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5255 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5256 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5258 iter = g_list_next(iter);
5260 // Remove original track and then update the display
5261 if ( track->is_route )
5262 vik_trw_layer_delete_route (vtl, track);
5264 vik_trw_layer_delete_track (vtl, track);
5265 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5267 g_list_free(newlists);
5271 * Split a track at the currently selected trackpoint
5273 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5275 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5276 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5277 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5281 * Split a track by its segments
5282 * Routes do not have segments so don't call this for routes
5284 static void trw_layer_split_segments ( gpointer pass_along[6] )
5286 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5287 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5294 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5297 for ( i = 0; i < ntracks; i++ ) {
5299 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5300 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5305 // Remove original track
5306 vik_trw_layer_delete_track ( vtl, trk );
5307 vik_layer_emit_update ( VIK_LAYER(vtl) );
5310 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5313 /* end of split/merge routines */
5316 * Delete adjacent track points at the same position
5317 * AKA Delete Dulplicates on the Properties Window
5319 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5321 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5323 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5324 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5326 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5331 gulong removed = vik_track_remove_dup_points ( trk );
5333 // Track has been updated so update tps:
5334 trw_layer_cancel_tps_of_track ( vtl, trk );
5336 // Inform user how much was deleted as it's not obvious from the normal view
5338 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5339 g_snprintf(str, 64, tmp_str, removed);
5340 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5342 vik_layer_emit_update ( VIK_LAYER(vtl) );
5346 * Delete adjacent track points with the same timestamp
5347 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5349 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5351 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5353 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5354 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5356 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5361 gulong removed = vik_track_remove_same_time_points ( trk );
5363 // Track has been updated so update tps:
5364 trw_layer_cancel_tps_of_track ( vtl, trk );
5366 // Inform user how much was deleted as it's not obvious from the normal view
5368 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5369 g_snprintf(str, 64, tmp_str, removed);
5370 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5372 vik_layer_emit_update ( VIK_LAYER(vtl) );
5378 static void trw_layer_reverse ( gpointer pass_along[6] )
5380 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5382 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5383 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5385 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5390 // Check valid track
5391 GList *trps = track->trackpoints;
5395 vik_track_reverse ( track );
5397 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5401 * Similar to trw_layer_enum_item, but this uses a sorted method
5404 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5406 GList **list = (GList**)udata;
5407 // *list = g_list_prepend(*all, key); //unsorted method
5408 // Sort named list alphabetically
5409 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5414 * Now Waypoint specific sort
5416 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5418 GList **list = (GList**)udata;
5419 // Sort named list alphabetically
5420 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5424 * Track specific sort
5426 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5428 GList **list = (GList**)udata;
5429 // Sort named list alphabetically
5430 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5435 gboolean has_same_track_name;
5436 const gchar *same_track_name;
5437 } same_track_name_udata;
5439 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5441 const gchar* namea = (const gchar*) aa;
5442 const gchar* nameb = (const gchar*) bb;
5445 gint result = strcmp ( namea, nameb );
5447 if ( result == 0 ) {
5448 // Found two names the same
5449 same_track_name_udata *user_data = udata;
5450 user_data->has_same_track_name = TRUE;
5451 user_data->same_track_name = namea;
5454 // Leave ordering the same
5459 * Find out if any tracks have the same name in this hash table
5461 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5463 // Sort items by name, then compare if any next to each other are the same
5465 GList *track_names = NULL;
5466 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5469 if ( ! track_names )
5472 same_track_name_udata udata;
5473 udata.has_same_track_name = FALSE;
5475 // Use sort routine to traverse list comparing items
5476 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5477 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5478 // Still no tracks...
5482 return udata.has_same_track_name;
5486 * Force unqiue track names for the track table specified
5487 * Note the panel is a required parameter to enable the update of the names displayed
5488 * Specify if on tracks or else on routes
5490 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5492 // . Search list for an instance of repeated name
5493 // . get track of this name
5494 // . create new name
5495 // . rename track & update equiv. treeview iter
5496 // . repeat until all different
5498 same_track_name_udata udata;
5500 GList *track_names = NULL;
5501 udata.has_same_track_name = FALSE;
5502 udata.same_track_name = NULL;
5504 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5507 if ( ! track_names )
5510 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5512 // Still no tracks...
5513 if ( ! dummy_list1 )
5516 while ( udata.has_same_track_name ) {
5518 // Find a track with the same name
5521 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5523 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5527 g_critical("Houston, we've had a problem.");
5528 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5529 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5534 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5535 vik_track_set_name ( trk, newname );
5541 // Need want key of it for treeview update
5542 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5544 if ( trkf && udataU.uuid ) {
5548 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5550 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5553 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5554 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5555 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5560 // Start trying to find same names again...
5562 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5563 udata.has_same_track_name = FALSE;
5564 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5566 // No tracks any more - give up searching
5567 if ( ! dummy_list2 )
5568 udata.has_same_track_name = FALSE;
5572 vik_layers_panel_emit_update ( vlp );
5578 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5580 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5583 // Ensure list of track names offered is unique
5584 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5585 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5586 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5587 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5593 // Sort list alphabetically for better presentation
5594 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5597 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5601 // Get list of items to delete from the user
5602 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5605 _("Delete Selection"),
5606 _("Select tracks to delete"));
5609 // Delete requested tracks
5610 // since specificly requested, IMHO no need for extra confirmation
5611 if ( delete_list ) {
5613 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5614 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5615 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5617 g_list_free(delete_list);
5618 vik_layer_emit_update( VIK_LAYER(vtl) );
5625 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5627 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5630 // Ensure list of track names offered is unique
5631 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5632 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5633 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5634 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5640 // Sort list alphabetically for better presentation
5641 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5644 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5648 // Get list of items to delete from the user
5649 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5652 _("Delete Selection"),
5653 _("Select routes to delete") );
5656 // Delete requested routes
5657 // since specificly requested, IMHO no need for extra confirmation
5658 if ( delete_list ) {
5660 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5661 // This deletes first route it finds of that name (but uniqueness is enforced above)
5662 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5664 g_list_free(delete_list);
5665 vik_layer_emit_update( VIK_LAYER(vtl) );
5670 gboolean has_same_waypoint_name;
5671 const gchar *same_waypoint_name;
5672 } same_waypoint_name_udata;
5674 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5676 const gchar* namea = (const gchar*) aa;
5677 const gchar* nameb = (const gchar*) bb;
5680 gint result = strcmp ( namea, nameb );
5682 if ( result == 0 ) {
5683 // Found two names the same
5684 same_waypoint_name_udata *user_data = udata;
5685 user_data->has_same_waypoint_name = TRUE;
5686 user_data->same_waypoint_name = namea;
5689 // Leave ordering the same
5694 * Find out if any waypoints have the same name in this layer
5696 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5698 // Sort items by name, then compare if any next to each other are the same
5700 GList *waypoint_names = NULL;
5701 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5704 if ( ! waypoint_names )
5707 same_waypoint_name_udata udata;
5708 udata.has_same_waypoint_name = FALSE;
5710 // Use sort routine to traverse list comparing items
5711 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5712 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5713 // Still no waypoints...
5717 return udata.has_same_waypoint_name;
5721 * Force unqiue waypoint names for this layer
5722 * Note the panel is a required parameter to enable the update of the names displayed
5724 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5726 // . Search list for an instance of repeated name
5727 // . get waypoint of this name
5728 // . create new name
5729 // . rename waypoint & update equiv. treeview iter
5730 // . repeat until all different
5732 same_waypoint_name_udata udata;
5734 GList *waypoint_names = NULL;
5735 udata.has_same_waypoint_name = FALSE;
5736 udata.same_waypoint_name = NULL;
5738 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5741 if ( ! waypoint_names )
5744 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5746 // Still no waypoints...
5747 if ( ! dummy_list1 )
5750 while ( udata.has_same_waypoint_name ) {
5752 // Find a waypoint with the same name
5753 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5757 g_critical("Houston, we've had a problem.");
5758 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5759 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5764 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5765 vik_waypoint_set_name ( waypoint, newname );
5768 udataU.wp = waypoint;
5771 // Need want key of it for treeview update
5772 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5774 if ( wpf && udataU.uuid ) {
5776 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5779 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5780 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5781 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5786 // Start trying to find same names again...
5787 waypoint_names = NULL;
5788 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5789 udata.has_same_waypoint_name = FALSE;
5790 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5792 // No waypoints any more - give up searching
5793 if ( ! dummy_list2 )
5794 udata.has_same_waypoint_name = FALSE;
5798 vik_layers_panel_emit_update ( vlp );
5804 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5806 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5809 // Ensure list of waypoint names offered is unique
5810 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5811 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5812 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5813 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5819 // Sort list alphabetically for better presentation
5820 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5822 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5826 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5828 // Get list of items to delete from the user
5829 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5832 _("Delete Selection"),
5833 _("Select waypoints to delete"));
5836 // Delete requested waypoints
5837 // since specificly requested, IMHO no need for extra confirmation
5838 if ( delete_list ) {
5840 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5841 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5842 trw_layer_delete_waypoint_by_name (vtl, l->data);
5844 g_list_free(delete_list);
5845 vik_layer_emit_update( VIK_LAYER(vtl) );
5850 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5852 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5854 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5857 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5859 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5862 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
5863 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5867 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
5869 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5872 if ( !strncmp(wp->comment, "http", 4) ) {
5873 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
5874 } else if ( !strncmp(wp->description, "http", 4) ) {
5875 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
5879 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
5881 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5883 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
5885 // No actual change to the name supplied
5886 if (strcmp(newname, wp->name) == 0 )
5889 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5892 // An existing waypoint has been found with the requested name
5893 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5894 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
5899 // Update WP name and refresh the treeview
5900 vik_waypoint_set_name (wp, newname);
5902 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5903 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5906 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5911 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5913 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
5915 // No actual change to the name supplied
5916 if (strcmp(newname, trk->name) == 0)
5919 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
5922 // An existing track has been found with the requested name
5923 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5924 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
5928 // Update track name and refresh GUI parts
5929 vik_track_set_name (trk, newname);
5931 // Update any subwindows that could be displaying this track which has changed name
5932 // Only one Track Edit Window
5933 if ( l->current_tp_track == trk && l->tpwin ) {
5934 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5936 // Property Dialog of the track
5937 vik_trw_layer_propwin_update ( trk );
5939 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5940 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5943 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5948 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5950 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
5952 // No actual change to the name supplied
5953 if (strcmp(newname, trk->name) == 0)
5956 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
5959 // An existing track has been found with the requested name
5960 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5961 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
5965 // Update track name and refresh GUI parts
5966 vik_track_set_name (trk, newname);
5968 // Update any subwindows that could be displaying this track which has changed name
5969 // Only one Track Edit Window
5970 if ( l->current_tp_track == trk && l->tpwin ) {
5971 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5973 // Property Dialog of the track
5974 vik_trw_layer_propwin_update ( trk );
5976 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5977 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5980 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5987 static gboolean is_valid_geocache_name ( gchar *str )
5989 gint len = strlen ( str );
5990 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]));
5993 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
5995 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5996 a_acquire_set_filter_track ( trk );
5999 #ifdef VIK_CONFIG_GOOGLE
6000 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6002 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6003 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6006 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6008 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6010 gchar *escaped = uri_escape ( tr->comment );
6011 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6012 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6019 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6020 /* viewpoint is now available instead */
6021 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6023 static gpointer pass_along[8];
6025 gboolean rv = FALSE;
6028 pass_along[1] = vlp;
6029 pass_along[2] = GINT_TO_POINTER (subtype);
6030 pass_along[3] = sublayer;
6031 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6032 pass_along[5] = vvp;
6033 pass_along[6] = iter;
6034 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6036 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6040 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6042 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6043 gtk_widget_show ( item );
6045 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6046 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6047 if (tr && tr->property_dialog)
6048 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6050 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6051 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6052 if (tr && tr->property_dialog)
6053 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6056 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6058 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6059 gtk_widget_show ( item );
6061 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6063 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6064 gtk_widget_show ( item );
6066 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6068 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6069 gtk_widget_show ( item );
6071 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6073 gboolean separator_created = FALSE;
6075 /* could be a right-click using the tool */
6076 if ( vlp != NULL ) {
6077 item = gtk_menu_item_new ();
6078 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6079 gtk_widget_show ( item );
6081 separator_created = TRUE;
6083 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6084 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6085 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6086 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6087 gtk_widget_show ( item );
6090 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6092 if ( wp && wp->name ) {
6093 if ( is_valid_geocache_name ( wp->name ) ) {
6095 if ( !separator_created ) {
6096 item = gtk_menu_item_new ();
6097 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6098 gtk_widget_show ( item );
6099 separator_created = TRUE;
6102 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6104 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6105 gtk_widget_show ( item );
6109 if ( wp && wp->image )
6111 if ( !separator_created ) {
6112 item = gtk_menu_item_new ();
6113 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6114 gtk_widget_show ( item );
6115 separator_created = TRUE;
6118 // Set up image paramater
6119 pass_along[5] = wp->image;
6121 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6122 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
6123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6124 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6125 gtk_widget_show ( item );
6127 #ifdef VIK_CONFIG_GEOTAG
6128 GtkWidget *geotag_submenu = gtk_menu_new ();
6129 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6131 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6132 gtk_widget_show ( item );
6133 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6135 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6137 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6138 gtk_widget_show ( item );
6140 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6141 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6142 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6143 gtk_widget_show ( item );
6149 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6150 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6151 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6152 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6154 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6155 gtk_widget_show ( item );
6162 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6163 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6165 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6166 gtk_widget_show ( item );
6167 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6168 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6169 gtk_widget_set_sensitive ( item, TRUE );
6171 gtk_widget_set_sensitive ( item, FALSE );
6174 item = gtk_menu_item_new ();
6175 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6176 gtk_widget_show ( item );
6179 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6182 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6183 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6185 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6186 gtk_widget_show ( item );
6189 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6191 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6192 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6193 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6194 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6195 gtk_widget_show ( item );
6197 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6198 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6199 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6200 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6201 gtk_widget_show ( item );
6203 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6204 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6205 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6206 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6207 gtk_widget_show ( item );
6209 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6210 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6211 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6212 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6213 gtk_widget_show ( item );
6216 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6220 if ( l->current_track && !l->current_track->is_route ) {
6221 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6222 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6223 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6224 gtk_widget_show ( item );
6226 item = gtk_menu_item_new ();
6227 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6228 gtk_widget_show ( item );
6231 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6232 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6233 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6234 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6235 gtk_widget_show ( item );
6237 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6238 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6239 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6240 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6241 gtk_widget_show ( item );
6242 // Make it available only when a new track *not* already in progress
6243 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6245 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6246 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6247 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6248 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6249 gtk_widget_show ( item );
6251 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6255 gtk_widget_show ( item );
6258 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6262 if ( l->current_track && l->current_track->is_route ) {
6263 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6264 // Reuse finish track method
6265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6267 gtk_widget_show ( item );
6269 item = gtk_menu_item_new ();
6270 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6271 gtk_widget_show ( item );
6274 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6275 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6276 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6277 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6278 gtk_widget_show ( item );
6280 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6281 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6282 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6283 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6284 gtk_widget_show ( item );
6285 // Make it available only when a new track *not* already in progress
6286 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6288 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6289 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6291 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6292 gtk_widget_show ( item );
6294 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6295 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6297 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6298 gtk_widget_show ( item );
6301 GtkWidget *upload_submenu = gtk_menu_new ();
6303 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6305 item = gtk_menu_item_new ();
6306 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6307 gtk_widget_show ( item );
6309 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6310 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6311 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6312 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6313 if ( l->current_track ) {
6314 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6315 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6316 gtk_widget_show ( item );
6319 item = gtk_menu_item_new ();
6320 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6321 gtk_widget_show ( item );
6324 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6325 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6327 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6328 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6329 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6330 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6331 gtk_widget_show ( item );
6333 GtkWidget *goto_submenu;
6334 goto_submenu = gtk_menu_new ();
6335 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6336 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6337 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6338 gtk_widget_show ( item );
6339 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6341 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6342 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6343 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6344 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6345 gtk_widget_show ( item );
6347 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6348 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6349 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6350 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6351 gtk_widget_show ( item );
6353 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6354 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6355 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6356 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6357 gtk_widget_show ( item );
6359 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6360 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6361 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6362 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6363 gtk_widget_show ( item );
6365 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6366 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6368 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6369 gtk_widget_show ( item );
6371 // Routes don't have speeds
6372 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6373 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6374 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6376 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6377 gtk_widget_show ( item );
6380 GtkWidget *combine_submenu;
6381 combine_submenu = gtk_menu_new ();
6382 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6383 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6384 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6385 gtk_widget_show ( item );
6386 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6388 // Routes don't have times or segments...
6389 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6390 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6391 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6392 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6393 gtk_widget_show ( item );
6395 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6396 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6397 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6398 gtk_widget_show ( item );
6401 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6402 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6403 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6404 gtk_widget_show ( item );
6406 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6407 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6409 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6410 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6411 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6412 gtk_widget_show ( item );
6414 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6415 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6417 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6418 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6419 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6420 gtk_widget_show ( item );
6422 GtkWidget *split_submenu;
6423 split_submenu = gtk_menu_new ();
6424 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6425 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6426 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6427 gtk_widget_show ( item );
6428 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6430 // Routes don't have times or segments...
6431 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6432 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6433 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6434 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6435 gtk_widget_show ( item );
6437 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6438 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6439 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6440 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6441 gtk_widget_show ( item );
6444 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6445 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6446 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6447 gtk_widget_show ( item );
6449 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6450 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6451 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6452 gtk_widget_show ( item );
6453 // Make it available only when a trackpoint is selected.
6454 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6456 GtkWidget *delete_submenu;
6457 delete_submenu = gtk_menu_new ();
6458 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6459 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6460 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6461 gtk_widget_show ( item );
6462 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6464 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6465 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6466 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6467 gtk_widget_show ( item );
6469 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6470 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6471 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6472 gtk_widget_show ( item );
6474 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6475 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6477 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6478 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6479 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6480 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6481 gtk_widget_show ( item );
6483 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6485 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6486 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6488 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6489 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
6490 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6491 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6492 gtk_widget_show ( item );
6495 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6496 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
6497 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6498 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6499 gtk_widget_show ( item );
6501 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6502 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6504 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6507 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6508 gtk_widget_show ( item );
6510 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6511 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6513 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6514 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6515 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6516 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6517 gtk_widget_show ( item );
6519 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6520 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6522 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6523 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6524 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6525 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6526 gtk_widget_show ( item );
6528 #ifdef VIK_CONFIG_GOOGLE
6529 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6530 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6531 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
6532 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6533 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6534 gtk_widget_show ( item );
6538 // ATM can't upload a single waypoint but can do waypoints to a GPS
6539 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6540 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6541 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6542 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6543 gtk_widget_show ( item );
6544 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6546 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6547 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6548 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6549 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6550 gtk_widget_show ( item );
6554 #ifdef VIK_CONFIG_GOOGLE
6555 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6557 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6558 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6560 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6561 gtk_widget_show ( item );
6565 // Some things aren't usable with routes
6566 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6567 #ifdef VIK_CONFIG_OPENSTREETMAP
6568 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6569 // Convert internal pointer into actual track for usage outside this file
6570 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6571 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6572 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6573 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6574 gtk_widget_show ( item );
6577 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6578 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6579 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6580 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6581 gtk_widget_show ( item );
6583 /* ATM This function is only available via the layers panel, due to needing a vlp */
6585 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6586 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6587 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6589 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6590 gtk_widget_show ( item );
6594 #ifdef VIK_CONFIG_GEOTAG
6595 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6596 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6597 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6598 gtk_widget_show ( item );
6602 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6603 // Only show on viewport popmenu when a trackpoint is selected
6604 if ( ! vlp && l->current_tpl ) {
6606 item = gtk_menu_item_new ();
6607 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6608 gtk_widget_show ( item );
6610 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6611 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6613 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6614 gtk_widget_show ( item );
6621 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6624 if (!vtl->current_tpl)
6626 if (!vtl->current_tpl->next)
6629 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6630 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6632 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6635 VikTrackpoint *tp_new = vik_trackpoint_new();
6636 struct LatLon ll_current, ll_next;
6637 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6638 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6640 /* main positional interpolation */
6641 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6642 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6644 /* Now other properties that can be interpolated */
6645 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6647 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6648 /* Note here the division is applied to each part, then added
6649 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6650 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6651 tp_new->has_timestamp = TRUE;
6654 if (tp_current->speed != NAN && tp_next->speed != NAN)
6655 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6657 /* TODO - improve interpolation of course, as it may not be correct.
6658 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6659 [similar applies if value is in radians] */
6660 if (tp_current->course != NAN && tp_next->course != NAN)
6661 tp_new->speed = (tp_current->course + tp_next->course)/2;
6663 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6665 /* Insert new point into the trackpoints list after the current TP */
6666 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6668 // Otherwise try routes
6669 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6673 gint index = g_list_index ( trk->trackpoints, tp_current );
6675 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6680 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6686 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6690 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6692 if ( vtl->current_tpl )
6694 vtl->current_tpl = NULL;
6695 vtl->current_tp_track = NULL;
6696 vtl->current_tp_id = NULL;
6697 vik_layer_emit_update(VIK_LAYER(vtl));
6701 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6703 g_assert ( vtl->tpwin != NULL );
6704 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6705 trw_layer_cancel_current_tp ( vtl, TRUE );
6707 if ( vtl->current_tpl == NULL )
6710 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6712 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6713 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6715 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6717 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6719 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6725 // Find available adjacent trackpoint
6726 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6728 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6729 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6731 // Delete current trackpoint
6732 vik_trackpoint_free ( vtl->current_tpl->data );
6733 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6735 // Set to current to the available adjacent trackpoint
6736 vtl->current_tpl = new_tpl;
6738 // Reset dialog with the available adjacent trackpoint
6739 if ( vtl->current_tp_track )
6740 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6742 vik_layer_emit_update(VIK_LAYER(vtl));
6746 // Delete current trackpoint
6747 vik_trackpoint_free ( vtl->current_tpl->data );
6748 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6749 trw_layer_cancel_current_tp ( vtl, FALSE );
6752 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6754 if ( vtl->current_tp_track )
6755 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6756 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
6758 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6760 if ( vtl->current_tp_track )
6761 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6762 vik_layer_emit_update(VIK_LAYER(vtl));
6764 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6766 trw_layer_insert_tp_after_current_tp ( vtl );
6767 vik_layer_emit_update(VIK_LAYER(vtl));
6769 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6770 vik_layer_emit_update(VIK_LAYER(vtl));
6773 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
6777 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6778 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
6779 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
6780 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
6781 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
6783 if ( vtl->current_tpl )
6784 if ( vtl->current_tp_track )
6785 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6786 /* set layer name and TP data */
6789 /***************************************************************************
6791 ***************************************************************************/
6793 /*** Utility data structures and functions ****/
6797 gint closest_x, closest_y;
6798 gpointer *closest_wp_id;
6799 VikWaypoint *closest_wp;
6805 gint closest_x, closest_y;
6806 gpointer closest_track_id;
6807 VikTrackpoint *closest_tp;
6812 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
6818 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
6820 // If waypoint has an image then use the image size to select
6822 gint slackx, slacky;
6823 slackx = wp->image_width / 2;
6824 slacky = wp->image_height / 2;
6826 if ( x <= params->x + slackx && x >= params->x - slackx
6827 && y <= params->y + slacky && y >= params->y - slacky ) {
6828 params->closest_wp_id = id;
6829 params->closest_wp = wp;
6830 params->closest_x = x;
6831 params->closest_y = y;
6834 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
6835 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
6836 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6838 params->closest_wp_id = id;
6839 params->closest_wp = wp;
6840 params->closest_x = x;
6841 params->closest_y = y;
6845 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
6847 GList *tpl = t->trackpoints;
6856 tp = VIK_TRACKPOINT(tpl->data);
6858 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
6860 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
6861 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
6862 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6864 params->closest_track_id = id;
6865 params->closest_tp = tp;
6866 params->closest_tpl = tpl;
6867 params->closest_x = x;
6868 params->closest_y = y;
6874 // ATM: Leave this as 'Track' only.
6875 // Not overly bothered about having a snap to route trackpoint capability
6876 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6878 TPSearchParams params;
6882 params.closest_track_id = NULL;
6883 params.closest_tp = NULL;
6884 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6885 return params.closest_tp;
6888 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6890 WPSearchParams params;
6894 params.closest_wp = NULL;
6895 params.closest_wp_id = NULL;
6896 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6897 return params.closest_wp;
6901 // Some forward declarations
6902 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
6903 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
6904 static void marker_end_move ( tool_ed_t *t );
6907 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6911 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6913 // Here always allow snapping back to the original location
6914 // this is useful when one decides not to move the thing afterall
6915 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
6918 if ( event->state & GDK_CONTROL_MASK )
6920 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6922 new_coord = tp->coord;
6926 if ( event->state & GDK_SHIFT_MASK )
6928 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6930 new_coord = wp->coord;
6934 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6936 marker_moveto ( t, x, y );
6943 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6945 if ( t->holding && event->button == 1 )
6948 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6951 if ( event->state & GDK_CONTROL_MASK )
6953 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6955 new_coord = tp->coord;
6959 if ( event->state & GDK_SHIFT_MASK )
6961 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6963 new_coord = wp->coord;
6966 marker_end_move ( t );
6968 // Determine if working on a waypoint or a trackpoint
6969 if ( t->is_waypoint )
6970 vtl->current_wp->coord = new_coord;
6972 if ( vtl->current_tpl ) {
6973 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6976 if ( vtl->current_tp_track )
6977 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6982 vtl->current_wp = NULL;
6983 vtl->current_wp_id = NULL;
6984 trw_layer_cancel_current_tp ( vtl, FALSE );
6986 vik_layer_emit_update ( VIK_LAYER(vtl) );
6993 Returns true if a waypoint or track is found near the requested event position for this particular layer
6994 The item found is automatically selected
6995 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
6997 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
6999 if ( event->button != 1 )
7002 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7005 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7008 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7010 if (vtl->waypoints_visible) {
7011 WPSearchParams wp_params;
7012 wp_params.vvp = vvp;
7013 wp_params.x = event->x;
7014 wp_params.y = event->y;
7015 wp_params.closest_wp_id = NULL;
7016 wp_params.closest_wp = NULL;
7018 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7020 if ( wp_params.closest_wp ) {
7023 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7025 // Too easy to move it so must be holding shift to start immediately moving it
7026 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7027 if ( event->state & GDK_SHIFT_MASK ||
7028 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7029 // Put into 'move buffer'
7030 // NB vvp & vw already set in tet
7031 tet->vtl = (gpointer)vtl;
7032 tet->is_waypoint = TRUE;
7034 marker_begin_move (tet, event->x, event->y);
7037 vtl->current_wp = wp_params.closest_wp;
7038 vtl->current_wp_id = wp_params.closest_wp_id;
7040 vik_layer_emit_update ( VIK_LAYER(vtl) );
7046 // Used for both track and route lists
7047 TPSearchParams tp_params;
7048 tp_params.vvp = vvp;
7049 tp_params.x = event->x;
7050 tp_params.y = event->y;
7051 tp_params.closest_track_id = NULL;
7052 tp_params.closest_tp = NULL;
7054 if (vtl->tracks_visible) {
7055 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7057 if ( tp_params.closest_tp ) {
7059 // Always select + highlight the track
7060 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7062 tet->is_waypoint = FALSE;
7064 // Select the Trackpoint
7065 // Can move it immediately when control held or it's the previously selected tp
7066 if ( event->state & GDK_CONTROL_MASK ||
7067 vtl->current_tpl == tp_params.closest_tpl ) {
7068 // Put into 'move buffer'
7069 // NB vvp & vw already set in tet
7070 tet->vtl = (gpointer)vtl;
7071 marker_begin_move (tet, event->x, event->y);
7074 vtl->current_tpl = tp_params.closest_tpl;
7075 vtl->current_tp_id = tp_params.closest_track_id;
7076 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7078 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7081 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7083 vik_layer_emit_update ( VIK_LAYER(vtl) );
7088 // Try again for routes
7089 if (vtl->routes_visible) {
7090 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7092 if ( tp_params.closest_tp ) {
7094 // Always select + highlight the track
7095 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7097 tet->is_waypoint = FALSE;
7099 // Select the Trackpoint
7100 // Can move it immediately when control held or it's the previously selected tp
7101 if ( event->state & GDK_CONTROL_MASK ||
7102 vtl->current_tpl == tp_params.closest_tpl ) {
7103 // Put into 'move buffer'
7104 // NB vvp & vw already set in tet
7105 tet->vtl = (gpointer)vtl;
7106 marker_begin_move (tet, event->x, event->y);
7109 vtl->current_tpl = tp_params.closest_tpl;
7110 vtl->current_tp_id = tp_params.closest_track_id;
7111 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7113 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7116 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7118 vik_layer_emit_update ( VIK_LAYER(vtl) );
7123 /* these aren't the droids you're looking for */
7124 vtl->current_wp = NULL;
7125 vtl->current_wp_id = NULL;
7126 trw_layer_cancel_current_tp ( vtl, FALSE );
7129 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7134 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7136 if ( event->button != 3 )
7139 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7142 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7145 /* Post menu for the currently selected item */
7147 /* See if a track is selected */
7148 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7149 if ( track && track->visible ) {
7151 if ( track->name ) {
7153 if ( vtl->track_right_click_menu )
7154 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
7156 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7163 if ( track->is_route )
7164 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7166 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7168 if ( trkf && udataU.uuid ) {
7171 if ( track->is_route )
7172 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7174 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7176 trw_layer_sublayer_add_menu_items ( vtl,
7177 vtl->track_right_click_menu,
7179 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7185 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7191 /* See if a waypoint is selected */
7192 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7193 if ( waypoint && waypoint->visible ) {
7194 if ( waypoint->name ) {
7196 if ( vtl->wp_right_click_menu )
7197 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
7199 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7202 udata.wp = waypoint;
7205 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7207 if ( wpf && udata.uuid ) {
7208 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7210 trw_layer_sublayer_add_menu_items ( vtl,
7211 vtl->wp_right_click_menu,
7213 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7218 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7227 /* background drawing hook, to be passed the viewport */
7228 static gboolean tool_sync_done = TRUE;
7230 static gboolean tool_sync(gpointer data)
7232 VikViewport *vvp = data;
7233 gdk_threads_enter();
7234 vik_viewport_sync(vvp);
7235 tool_sync_done = TRUE;
7236 gdk_threads_leave();
7240 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7243 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7244 gdk_gc_set_function ( t->gc, GDK_INVERT );
7245 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7246 vik_viewport_sync(t->vvp);
7251 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7253 VikViewport *vvp = t->vvp;
7254 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7255 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7259 if (tool_sync_done) {
7260 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7261 tool_sync_done = FALSE;
7265 static void marker_end_move ( tool_ed_t *t )
7267 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7268 g_object_unref ( t->gc );
7272 /*** Edit waypoint ****/
7274 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7276 tool_ed_t *t = g_new(tool_ed_t, 1);
7282 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7284 WPSearchParams params;
7285 tool_ed_t *t = data;
7286 VikViewport *vvp = t->vvp;
7288 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7295 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7298 if ( vtl->current_wp && vtl->current_wp->visible )
7300 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7302 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7304 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7305 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7307 if ( event->button == 3 )
7308 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7310 marker_begin_move(t, event->x, event->y);
7317 params.x = event->x;
7318 params.y = event->y;
7319 params.closest_wp_id = NULL;
7320 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7321 params.closest_wp = NULL;
7322 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7323 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7325 // how do we get here?
7326 marker_begin_move(t, event->x, event->y);
7327 g_critical("shouldn't be here");
7330 else if ( params.closest_wp )
7332 if ( event->button == 3 )
7333 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7335 vtl->waypoint_rightclick = FALSE;
7337 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7339 vtl->current_wp = params.closest_wp;
7340 vtl->current_wp_id = params.closest_wp_id;
7342 /* could make it so don't update if old WP is off screen and new is null but oh well */
7343 vik_layer_emit_update ( VIK_LAYER(vtl) );
7347 vtl->current_wp = NULL;
7348 vtl->current_wp_id = NULL;
7349 vtl->waypoint_rightclick = FALSE;
7350 vik_layer_emit_update ( VIK_LAYER(vtl) );
7354 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7356 tool_ed_t *t = data;
7357 VikViewport *vvp = t->vvp;
7359 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7364 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7367 if ( event->state & GDK_CONTROL_MASK )
7369 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7371 new_coord = tp->coord;
7375 if ( event->state & GDK_SHIFT_MASK )
7377 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7378 if ( wp && wp != vtl->current_wp )
7379 new_coord = wp->coord;
7384 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7386 marker_moveto ( t, x, y );
7393 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7395 tool_ed_t *t = data;
7396 VikViewport *vvp = t->vvp;
7398 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7401 if ( t->holding && event->button == 1 )
7404 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7407 if ( event->state & GDK_CONTROL_MASK )
7409 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7411 new_coord = tp->coord;
7415 if ( event->state & GDK_SHIFT_MASK )
7417 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7418 if ( wp && wp != vtl->current_wp )
7419 new_coord = wp->coord;
7422 marker_end_move ( t );
7424 vtl->current_wp->coord = new_coord;
7425 vik_layer_emit_update ( VIK_LAYER(vtl) );
7428 /* PUT IN RIGHT PLACE!!! */
7429 if ( event->button == 3 && vtl->waypoint_rightclick )
7431 if ( vtl->wp_right_click_menu )
7432 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7433 if ( vtl->current_wp ) {
7434 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7435 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 );
7436 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7438 vtl->waypoint_rightclick = FALSE;
7443 /*** New track ****/
7445 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7452 GdkDrawable *drawable;
7458 * Draw specified pixmap
7460 static gboolean draw_sync ( gpointer data )
7462 draw_sync_t *ds = (draw_sync_t*) data;
7463 // Sometimes don't want to draw
7464 // normally because another update has taken precedent such as panning the display
7465 // which means this pixmap is no longer valid
7466 if ( ds->vtl->draw_sync_do ) {
7467 gdk_threads_enter();
7468 gdk_draw_drawable (ds->drawable,
7471 0, 0, 0, 0, -1, -1);
7472 ds->vtl->draw_sync_done = TRUE;
7473 gdk_threads_leave();
7478 static gchar* distance_string (gdouble distance)
7482 /* draw label with distance */
7483 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7484 switch (dist_units) {
7485 case VIK_UNITS_DISTANCE_MILES:
7486 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7487 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7488 } else if (distance < 1609.4) {
7489 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7491 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7495 // VIK_UNITS_DISTANCE_KILOMETRES
7496 if (distance >= 1000 && distance < 100000) {
7497 g_sprintf(str, "%3.2f km", distance/1000.0);
7498 } else if (distance < 1000) {
7499 g_sprintf(str, "%d m", (int)distance);
7501 g_sprintf(str, "%d km", (int)distance/1000);
7505 return g_strdup (str);
7509 * Actually set the message in statusbar
7511 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
7513 // Only show elevation data when track has some elevation properties
7514 gchar str_gain_loss[64];
7515 str_gain_loss[0] = '\0';
7516 gchar str_last_step[64];
7517 str_last_step[0] = '\0';
7518 gchar *str_total = distance_string (distance);
7520 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7521 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7522 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7524 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7527 if ( last_step > 0 ) {
7528 gchar *tmp = distance_string (last_step);
7529 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
7533 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
7535 // Write with full gain/loss information
7536 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
7537 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
7539 g_free ( str_total );
7543 * Figure out what information should be set in the statusbar and then write it
7545 static void update_statusbar ( VikTrwLayer *vtl )
7547 // Get elevation data
7548 gdouble elev_gain, elev_loss;
7549 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7551 /* Find out actual distance of current track */
7552 gdouble distance = vik_track_get_length (vtl->current_track);
7554 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
7558 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7560 /* if we haven't sync'ed yet, we don't have time to do more. */
7561 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7562 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7563 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7565 static GdkPixmap *pixmap = NULL;
7567 // Need to check in case window has been resized
7568 w1 = vik_viewport_get_width(vvp);
7569 h1 = vik_viewport_get_height(vvp);
7571 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7573 gdk_drawable_get_size (pixmap, &w2, &h2);
7574 if (w1 != w2 || h1 != h2) {
7575 g_object_unref ( G_OBJECT ( pixmap ) );
7576 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7579 // Reset to background
7580 gdk_draw_drawable (pixmap,
7581 vtl->current_track_newpoint_gc,
7582 vik_viewport_get_pixmap(vvp),
7583 0, 0, 0, 0, -1, -1);
7585 draw_sync_t *passalong;
7588 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7590 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7591 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7592 // thus when we come to reset to the background it would include what we have already drawn!!
7593 gdk_draw_line ( pixmap,
7594 vtl->current_track_newpoint_gc,
7595 x1, y1, event->x, event->y );
7596 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7598 /* Find out actual distance of current track */
7599 gdouble distance = vik_track_get_length (vtl->current_track);
7601 // Now add distance to where the pointer is //
7604 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7605 vik_coord_to_latlon ( &coord, &ll );
7606 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
7607 distance = distance + last_step;
7609 // Get elevation data
7610 gdouble elev_gain, elev_loss;
7611 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7613 // Adjust elevation data (if available) for the current pointer position
7615 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7616 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7617 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7618 // Adjust elevation of last track point
7619 if ( elev_new > last_tpt->altitude )
7621 elev_gain += elev_new - last_tpt->altitude;
7624 elev_loss += last_tpt->altitude - elev_new;
7628 gchar *str = distance_string (distance);
7630 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7631 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
7633 pango_layout_set_text (pl, str, -1);
7635 pango_layout_get_pixel_size ( pl, &wd, &hd );
7638 // offset from cursor a bit depending on font size
7642 // Create a background block to make the text easier to read over the background map
7643 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7644 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7645 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7647 g_object_unref ( G_OBJECT ( pl ) );
7648 g_object_unref ( G_OBJECT ( background_block_gc ) );
7650 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7651 passalong->vtl = vtl;
7652 passalong->pixmap = pixmap;
7653 passalong->drawable = GTK_WIDGET(vvp)->window;
7654 passalong->gc = vtl->current_track_newpoint_gc;
7658 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
7660 // Update statusbar with full gain/loss information
7661 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7665 // draw pixmap when we have time to
7666 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7667 vtl->draw_sync_done = FALSE;
7668 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7670 return VIK_LAYER_TOOL_ACK;
7673 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7675 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7676 vtl->current_track = NULL;
7677 vik_layer_emit_update ( VIK_LAYER(vtl) );
7679 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7681 if ( vtl->current_track->trackpoints )
7683 GList *last = g_list_last(vtl->current_track->trackpoints);
7684 g_free ( last->data );
7685 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7688 update_statusbar ( vtl );
7690 vik_layer_emit_update ( VIK_LAYER(vtl) );
7697 * Common function to handle trackpoint button requests on either a route or a track
7698 * . enables adding a point via normal click
7699 * . enables removal of last point via right click
7700 * . finishing of the track or route via double clicking
7702 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7706 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7709 if ( event->button == 2 ) {
7710 // As the display is panning, the new track pixmap is now invalid so don't draw it
7711 // otherwise this drawing done results in flickering back to an old image
7712 vtl->draw_sync_do = FALSE;
7716 if ( event->button == 3 )
7718 if ( !vtl->current_track )
7721 if ( vtl->current_track->trackpoints )
7723 GList *last = g_list_last(vtl->current_track->trackpoints);
7724 g_free ( last->data );
7725 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7727 update_statusbar ( vtl );
7729 vik_layer_emit_update ( VIK_LAYER(vtl) );
7733 if ( event->type == GDK_2BUTTON_PRESS )
7735 /* subtract last (duplicate from double click) tp then end */
7736 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7738 GList *last = g_list_last(vtl->current_track->trackpoints);
7739 g_free ( last->data );
7740 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7741 /* undo last, then end */
7742 vtl->current_track = NULL;
7744 vik_layer_emit_update ( VIK_LAYER(vtl) );
7748 tp = vik_trackpoint_new();
7749 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
7751 /* snap to other TP */
7752 if ( event->state & GDK_CONTROL_MASK )
7754 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7756 tp->coord = other_tp->coord;
7759 tp->newsegment = FALSE;
7760 tp->has_timestamp = FALSE;
7763 if ( vtl->current_track ) {
7764 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
7765 /* Auto attempt to get elevation from DEM data (if it's available) */
7766 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
7769 vtl->ct_x1 = vtl->ct_x2;
7770 vtl->ct_y1 = vtl->ct_y2;
7771 vtl->ct_x2 = event->x;
7772 vtl->ct_y2 = event->y;
7774 vik_layer_emit_update ( VIK_LAYER(vtl) );
7778 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7780 // ----------------------------------------------------- if current is a route - switch to new track
7781 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
7783 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
7784 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
7786 new_track_create_common ( vtl, name );
7791 return tool_new_track_or_route_click ( vtl, event, vvp );
7794 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7796 if ( event->button == 2 ) {
7797 // Pan moving ended - enable potential point drawing again
7798 vtl->draw_sync_do = TRUE;
7799 vtl->draw_sync_done = TRUE;
7803 /*** New route ****/
7805 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
7810 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7812 // -------------------------- if current is a track - switch to new route
7813 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
7815 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
7816 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
7817 new_route_create_common ( vtl, name );
7821 return tool_new_track_or_route_click ( vtl, event, vvp );
7824 /*** New waypoint ****/
7826 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7831 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7834 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7836 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
7837 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
7838 vik_layer_emit_update ( VIK_LAYER(vtl) );
7843 /*** Edit trackpoint ****/
7845 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
7847 tool_ed_t *t = g_new(tool_ed_t, 1);
7853 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7855 tool_ed_t *t = data;
7856 VikViewport *vvp = t->vvp;
7857 TPSearchParams params;
7858 /* OUTDATED DOCUMENTATION:
7859 find 5 pixel range on each side. then put these UTM, and a pointer
7860 to the winning track name (and maybe the winning track itself), and a
7861 pointer to the winning trackpoint, inside an array or struct. pass
7862 this along, do a foreach on the tracks which will do a foreach on the
7865 params.x = event->x;
7866 params.y = event->y;
7867 params.closest_track_id = NULL;
7868 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7869 params.closest_tp = NULL;
7871 if ( event->button != 1 )
7874 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7877 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
7880 if ( vtl->current_tpl )
7882 /* 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.) */
7883 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7884 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
7889 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
7891 if ( current_tr->visible &&
7892 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7893 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
7894 marker_begin_move ( t, event->x, event->y );
7900 if ( vtl->tracks_visible )
7901 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7903 if ( params.closest_tp )
7905 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
7906 vtl->current_tpl = params.closest_tpl;
7907 vtl->current_tp_id = params.closest_track_id;
7908 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
7909 trw_layer_tpwin_init ( vtl );
7910 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7911 vik_layer_emit_update ( VIK_LAYER(vtl) );
7915 if ( vtl->routes_visible )
7916 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
7918 if ( params.closest_tp )
7920 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
7921 vtl->current_tpl = params.closest_tpl;
7922 vtl->current_tp_id = params.closest_track_id;
7923 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
7924 trw_layer_tpwin_init ( vtl );
7925 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7926 vik_layer_emit_update ( VIK_LAYER(vtl) );
7930 /* these aren't the droids you're looking for */
7934 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7936 tool_ed_t *t = data;
7937 VikViewport *vvp = t->vvp;
7939 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7945 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7948 if ( event->state & GDK_CONTROL_MASK )
7950 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7951 if ( tp && tp != vtl->current_tpl->data )
7952 new_coord = tp->coord;
7954 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7957 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7958 marker_moveto ( t, x, y );
7966 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7968 tool_ed_t *t = data;
7969 VikViewport *vvp = t->vvp;
7971 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7973 if ( event->button != 1)
7978 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7981 if ( event->state & GDK_CONTROL_MASK )
7983 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7984 if ( tp && tp != vtl->current_tpl->data )
7985 new_coord = tp->coord;
7988 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7990 marker_end_move ( t );
7992 /* diff dist is diff from orig */
7994 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7996 vik_layer_emit_update ( VIK_LAYER(vtl) );
8003 #ifdef VIK_CONFIG_GOOGLE
8004 /*** Route Finder ***/
8005 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8010 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8013 if ( !vtl ) return FALSE;
8014 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8015 if ( event->button == 3 && vtl->route_finder_current_track ) {
8017 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8019 vtl->route_finder_coord = *new_end;
8021 vik_layer_emit_update ( VIK_LAYER(vtl) );
8022 /* remove last ' to:...' */
8023 if ( vtl->route_finder_current_track->comment ) {
8024 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8025 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8026 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8027 last_to - vtl->route_finder_current_track->comment - 1);
8028 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8033 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8034 struct LatLon start, end;
8035 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
8036 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
8039 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8040 vik_coord_to_latlon ( &(tmp), &end );
8041 vtl->route_finder_coord = tmp; /* for continuations */
8043 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8044 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8045 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8047 vtl->route_finder_check_added_track = TRUE;
8048 vtl->route_finder_started = FALSE;
8051 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
8052 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
8053 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
8054 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
8055 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
8056 // NB normally this returns a GPX Route - so subsequent usage of it must lookup via the routes hash
8057 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL, NULL );
8060 /* see if anything was done -- a track was added or appended to */
8061 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8062 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 ) );
8063 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8064 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8065 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8066 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8068 vtl->route_finder_added_track = NULL;
8069 vtl->route_finder_check_added_track = FALSE;
8070 vtl->route_finder_append = FALSE;
8072 vik_layer_emit_update ( VIK_LAYER(vtl) );
8074 vtl->route_finder_started = TRUE;
8075 vtl->route_finder_coord = tmp;
8076 vtl->route_finder_current_track = NULL;
8082 /*** Show picture ****/
8084 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8089 /* Params are: vvp, event, last match found or NULL */
8090 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8092 if ( wp->image && wp->visible )
8094 gint x, y, slackx, slacky;
8095 GdkEventButton *event = (GdkEventButton *) params[1];
8097 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8098 slackx = wp->image_width / 2;
8099 slacky = wp->image_height / 2;
8100 if ( x <= event->x + slackx && x >= event->x - slackx
8101 && y <= event->y + slacky && y >= event->y - slacky )
8103 params[2] = wp->image; /* we've found a match. however continue searching
8104 * since we want to find the last match -- that
8105 * is, the match that was drawn last. */
8110 static void trw_layer_show_picture ( gpointer pass_along[6] )
8112 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8114 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8117 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8118 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8119 g_free ( quoted_file );
8120 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8122 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() );
8123 g_error_free ( err );
8126 #endif /* WINDOWS */
8129 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8131 gpointer params[3] = { vvp, event, NULL };
8132 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8134 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8137 static gpointer pass_along[6];
8138 pass_along[0] = vtl;
8139 pass_along[5] = params[2];
8140 trw_layer_show_picture ( pass_along );
8141 return TRUE; /* found a match */
8144 return FALSE; /* go through other layers, searching for a match */
8147 /***************************************************************************
8149 ***************************************************************************/
8152 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8154 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8155 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8158 /* Structure for thumbnail creating data used in the background thread */
8160 VikTrwLayer *vtl; // Layer needed for redrawing
8161 GSList *pics; // Image list
8162 } thumbnail_create_thread_data;
8164 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8166 guint total = g_slist_length(tctd->pics), done = 0;
8167 while ( tctd->pics )
8169 a_thumbnails_create ( (gchar *) tctd->pics->data );
8170 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8172 return -1; /* Abort thread */
8174 tctd->pics = tctd->pics->next;
8177 // Redraw to show the thumbnails as they are now created
8178 if ( IS_VIK_LAYER(tctd->vtl) )
8179 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8184 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8186 while ( tctd->pics )
8188 g_free ( tctd->pics->data );
8189 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8194 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8196 if ( ! vtl->has_verified_thumbnails )
8198 GSList *pics = NULL;
8199 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8202 gint len = g_slist_length ( pics );
8203 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8204 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8207 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8209 (vik_thr_func) create_thumbnails_thread,
8211 (vik_thr_free_func) thumbnail_create_thread_free,
8219 static const gchar* my_track_colors ( gint ii )
8221 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8233 // Fast and reliable way of returning a colour
8234 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8237 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8239 GHashTableIter iter;
8240 gpointer key, value;
8244 g_hash_table_iter_init ( &iter, vtl->tracks );
8246 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8248 // Tracks get a random spread of colours if not already assigned
8249 if ( ! VIK_TRACK(value)->has_color ) {
8250 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8251 VIK_TRACK(value)->color = vtl->track_color;
8253 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8255 VIK_TRACK(value)->has_color = TRUE;
8258 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8261 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8267 g_hash_table_iter_init ( &iter, vtl->routes );
8269 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8271 // Routes get an intermix of reds
8272 if ( ! VIK_TRACK(value)->has_color ) {
8274 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8276 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8277 VIK_TRACK(value)->has_color = TRUE;
8280 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8286 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vp )
8288 trw_layer_verify_thumbnails ( vtl, vp );
8289 trw_layer_track_alloc_colors ( vtl );
8292 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8294 return vtl->coord_mode;
8298 * Uniquify the whole layer
8299 * Also requires the layers panel as the names shown there need updating too
8300 * Returns whether the operation was successful or not
8302 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8305 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8306 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8307 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8313 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8315 vik_coord_convert ( &(wp->coord), *dest_mode );
8318 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
8320 vik_track_convert ( tr, *dest_mode );
8323 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8325 if ( vtl->coord_mode != dest_mode )
8327 vtl->coord_mode = dest_mode;
8328 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8329 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8330 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
8334 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8336 vtl->menu_selection = selection;
8339 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8341 return (vtl->menu_selection);
8344 /* ----------- Downloading maps along tracks --------------- */
8346 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8348 /* TODO: calculating based on current size of viewport */
8349 const gdouble w_at_zoom_0_125 = 0.0013;
8350 const gdouble h_at_zoom_0_125 = 0.0011;
8351 gdouble zoom_factor = zoom_level/0.125;
8353 wh->lat = h_at_zoom_0_125 * zoom_factor;
8354 wh->lon = w_at_zoom_0_125 * zoom_factor;
8356 return 0; /* all OK */
8359 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8361 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8362 (dist->lat >= ABS(to->north_south - from->north_south)))
8365 VikCoord *coord = g_malloc(sizeof(VikCoord));
8366 coord->mode = VIK_COORD_LATLON;
8368 if (ABS(gradient) < 1) {
8369 if (from->east_west > to->east_west)
8370 coord->east_west = from->east_west - dist->lon;
8372 coord->east_west = from->east_west + dist->lon;
8373 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8375 if (from->north_south > to->north_south)
8376 coord->north_south = from->north_south - dist->lat;
8378 coord->north_south = from->north_south + dist->lat;
8379 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8385 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8387 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8388 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8390 VikCoord *next = from;
8392 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8394 list = g_list_prepend(list, next);
8400 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8402 typedef struct _Rect {
8407 #define GLRECT(iter) ((Rect *)((iter)->data))
8410 GList *rects_to_download = NULL;
8413 if (get_download_area_width(vvp, zoom_level, &wh))
8416 GList *iter = tr->trackpoints;
8420 gboolean new_map = TRUE;
8421 VikCoord *cur_coord, tl, br;
8424 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8426 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8427 rect = g_malloc(sizeof(Rect));
8430 rect->center = *cur_coord;
8431 rects_to_download = g_list_prepend(rects_to_download, rect);
8436 gboolean found = FALSE;
8437 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8438 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8449 GList *fillins = NULL;
8450 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8451 /* seems that ATM the function get_next_coord works only for LATLON */
8452 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8453 /* fill-ins for far apart points */
8454 GList *cur_rect, *next_rect;
8455 for (cur_rect = rects_to_download;
8456 (next_rect = cur_rect->next) != NULL;
8457 cur_rect = cur_rect->next) {
8458 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8459 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8460 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8464 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8467 GList *iter = fillins;
8469 cur_coord = (VikCoord *)(iter->data);
8470 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8471 rect = g_malloc(sizeof(Rect));
8474 rect->center = *cur_coord;
8475 rects_to_download = g_list_prepend(rects_to_download, rect);
8480 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8481 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8485 for (iter = fillins; iter; iter = iter->next)
8487 g_list_free(fillins);
8489 if (rects_to_download) {
8490 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8491 g_free(rect_iter->data);
8492 g_list_free(rects_to_download);
8496 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8499 gint selected_map, default_map;
8500 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8501 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8502 gint selected_zoom, default_zoom;
8506 VikTrwLayer *vtl = pass_along[0];
8507 VikLayersPanel *vlp = pass_along[1];
8509 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8510 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8512 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8516 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8518 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8519 int num_maps = g_list_length(vmls);
8522 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8526 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8527 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8529 gchar **np = map_names;
8530 VikMapsLayer **lp = map_layers;
8531 for (i = 0; i < num_maps; i++) {
8532 gboolean dup = FALSE;
8533 vml = (VikMapsLayer *)(vmls->data);
8534 for (j = 0; j < i; j++) { /* no duplicate allowed */
8535 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8542 *np++ = vik_maps_layer_get_map_label(vml);
8548 num_maps = lp - map_layers;
8550 for (default_map = 0; default_map < num_maps; default_map++) {
8551 /* TODO: check for parent layer's visibility */
8552 if (VIK_LAYER(map_layers[default_map])->visible)
8555 default_map = (default_map == num_maps) ? 0 : default_map;
8557 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8558 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8559 if (cur_zoom == zoom_vals[default_zoom])
8562 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8564 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8567 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8570 for (i = 0; i < num_maps; i++)
8571 g_free(map_names[i]);
8579 /**** lowest waypoint number calculation ***/
8580 static gint highest_wp_number_name_to_number(const gchar *name) {
8581 if ( strlen(name) == 3 ) {
8583 if ( n < 100 && name[0] != '0' )
8585 if ( n < 10 && name[0] != '0' )
8593 static void highest_wp_number_reset(VikTrwLayer *vtl)
8595 vtl->highest_wp_number = -1;
8598 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8600 /* if is bigger that top, add it */
8601 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8602 if ( new_wp_num > vtl->highest_wp_number )
8603 vtl->highest_wp_number = new_wp_num;
8606 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8608 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8609 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8610 if ( vtl->highest_wp_number == old_wp_num ) {
8612 vtl->highest_wp_number--;
8614 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8615 /* search down until we find something that *does* exist */
8617 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8618 vtl->highest_wp_number--;
8619 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8624 /* get lowest unused number */
8625 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8628 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8630 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8631 return g_strdup(buf);