2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
26 /* viktrwlayer.c -- 5000+ lines can make a difference in the state of things */
33 #include "vikmapslayer.h"
34 #include "vikgpslayer.h"
35 #include "viktrwlayer_tpwin.h"
36 #include "viktrwlayer_propwin.h"
37 #ifdef VIK_CONFIG_GEOTAG
38 #include "viktrwlayer_geotag.h"
39 #include "geotag_exif.h"
41 #include "garminsymbols.h"
42 #include "thumbnails.h"
43 #include "background.h"
48 #include "geonamessearch.h"
49 #ifdef VIK_CONFIG_OPENSTREETMAP
50 #include "osm-traces.h"
53 #include "datasources.h"
54 #include "datasource_gps.h"
57 #include "icons/icons.h"
71 #include <gdk/gdkkeysyms.h>
73 #include <glib/gstdio.h>
74 #include <glib/gi18n.h>
76 /* Relax some dependencies */
77 #if ! GLIB_CHECK_VERSION(2,12,0)
78 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
79 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
82 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
83 // This is currently broken as Google have disabled the KML output in Google Maps API v3
84 // It has been ifdefed out in the hope that Route Finding functionality will be restored one day...
85 // Only have 'JSON' and 'XML' see:
86 // https://developers.google.com/maps/documentation/directions/#DirectionsResponses
87 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
90 #define VIK_TRW_LAYER_TRACK_GC 16
91 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
92 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
93 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
94 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
95 #define VIK_TRW_LAYER_TRACK_GC_SLOW 13
96 #define VIK_TRW_LAYER_TRACK_GC_AVER 14
97 #define VIK_TRW_LAYER_TRACK_GC_FAST 15
99 #define DRAWMODE_BY_TRACK 0
100 #define DRAWMODE_BY_SPEED 1
101 #define DRAWMODE_ALL_BLACK 2
102 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
103 // as we are (re)calculating the colour for every point
108 /* this is how it knows when you click if you are clicking close to a trackpoint. */
109 #define TRACKPOINT_SIZE_APPROX 5
110 #define WAYPOINT_SIZE_APPROX 5
112 #define MIN_STOP_LENGTH 15
113 #define MAX_STOP_LENGTH 86400
114 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
115 /* this is multiplied by user-inputted value from 1-100. */
117 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
119 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
121 FS_XX_SMALL = 0, // 'xx-small'
124 FS_MEDIUM, // DEFAULT
131 struct _VikTrwLayer {
134 GHashTable *tracks_iters;
135 GHashTable *waypoints_iters;
136 GHashTable *waypoints;
137 GtkTreeIter waypoints_iter, tracks_iter;
138 gboolean tracks_visible, waypoints_visible;
141 guint8 drawelevation;
142 guint8 elevation_factor;
146 guint8 line_thickness;
147 guint8 bg_line_thickness;
151 gboolean wp_draw_symbols;
152 font_size_t wp_font_size;
154 gdouble track_draw_speed_factor;
156 GdkGC *current_track_gc;
157 // Separate GC for a track's potential new point as drawn via separate method
158 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
159 GdkGC *current_track_newpoint_gc;
162 GdkGC *waypoint_text_gc;
163 GdkGC *waypoint_bg_gc;
164 VikTrack *current_track;
165 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
166 gboolean draw_sync_done;
167 gboolean draw_sync_do;
169 VikCoordMode coord_mode;
171 /* wp editing tool */
172 VikWaypoint *current_wp;
173 gpointer current_wp_id;
175 gboolean waypoint_rightclick;
177 /* track editing tool */
179 VikTrack *current_tp_track;
180 gpointer current_tp_id;
181 VikTrwLayerTpwin *tpwin;
183 /* track editing tool -- more specifically, moving tps */
186 /* route finder tool */
187 gboolean route_finder_started;
188 VikCoord route_finder_coord;
189 gboolean route_finder_check_added_track;
190 VikTrack *route_finder_added_track;
191 VikTrack *route_finder_current_track;
192 gboolean route_finder_append;
199 guint16 image_cache_size;
201 /* for waypoint text */
202 PangoLayout *wplabellayout;
204 gboolean has_verified_thumbnails;
206 GtkMenu *wp_right_click_menu;
207 GtkMenu *track_right_click_menu;
210 VikStdLayerMenuItem menu_selection;
212 gint highest_wp_number;
215 /* A caached waypoint image. */
218 gchar *image; /* filename */
221 struct DrawingParams {
225 guint16 width, height;
226 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 gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
239 static void trw_layer_find_maxmin_tracks ( const gchar *name, 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 gchar *name, VikTrack *track, struct DrawingParams *dp );
246 static void trw_layer_draw_waypoint ( const gchar *name, 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_auto_waypoints_view ( gpointer lav[2] );
277 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
278 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
279 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
280 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
281 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
282 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
283 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
284 #ifdef VIK_CONFIG_GEOTAG
285 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
286 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
287 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
288 static void trw_layer_geotagging ( gpointer lav[2] );
290 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
291 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
292 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
294 #ifdef VIK_CONFIG_OPENSTREETMAP
295 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
297 #ifdef VIK_CONFIG_GEOCACHES
298 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
300 #ifdef VIK_CONFIG_GEOTAG
301 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
303 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
304 static void trw_layer_gps_upload ( gpointer lav[2] );
307 static void trw_layer_properties_item ( gpointer pass_along[7] );
308 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
309 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
311 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
312 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
313 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
315 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
316 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
317 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
318 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
320 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
321 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
322 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
323 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
324 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
325 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
326 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
327 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
328 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
329 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
330 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
331 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
332 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
333 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
334 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
335 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
336 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
337 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
338 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
339 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
340 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
341 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
344 static void cached_pixbuf_free ( CachedPixbuf *cp );
345 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
347 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
348 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
350 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
351 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
353 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
354 static void highest_wp_number_reset(VikTrwLayer *vtl);
355 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
356 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
358 // Note for the following tool GtkRadioActionEntry texts:
359 // the very first text value is an internal name not displayed anywhere
360 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
361 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
362 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
363 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
364 static VikToolInterface trw_layer_tools[] = {
365 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
366 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
367 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
369 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
371 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
372 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
373 (VikToolMouseFunc) tool_new_track_click,
374 (VikToolMouseMoveFunc) tool_new_track_move,
375 (VikToolMouseFunc) tool_new_track_release,
376 (VikToolKeyFunc) tool_new_track_key_press,
377 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
378 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
380 { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "<control><shift>B", N_("Begin Track"), 0 },
381 (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
382 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL,
384 GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
386 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
387 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
388 (VikToolMouseFunc) tool_edit_waypoint_click,
389 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
390 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
392 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
394 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
395 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
396 (VikToolMouseFunc) tool_edit_trackpoint_click,
397 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
398 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
400 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
402 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
403 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
404 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
406 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
408 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
409 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
410 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
411 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
413 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
416 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
418 /****** PARAMETERS ******/
420 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
421 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
423 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
424 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
427 static VikLayerParamScale params_scales[] = {
428 /* min max step digits */
429 { 1, 10, 1, 0 }, /* line_thickness */
430 { 0, 100, 1, 0 }, /* track draw speed factor */
431 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
432 /* 5 * step == how much to turn */
433 { 16, 128, 3.2, 0 }, /* image_size */
434 { 0, 255, 5, 0 }, /* image alpha */
435 { 5, 500, 5, 0 }, /* image cache_size */
436 { 0, 8, 1, 0 }, /* image cache_size */
437 { 1, 64, 1, 0 }, /* wpsize */
438 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
439 { 1, 100, 1, 0 }, /* stop_length */
442 static gchar* params_font_sizes[] = {
443 N_("Extra Extra Small"),
449 N_("Extra Extra Large"),
452 VikLayerParam trw_layer_params[] = {
453 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
454 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
456 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
457 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
458 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
459 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
460 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
462 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
463 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
465 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
466 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
467 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
468 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
470 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
471 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
472 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
473 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
474 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
475 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
476 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
477 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
478 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
480 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
481 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
482 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
483 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
486 enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_DE, PARAM_EF, PARAM_DS, PARAM_SL, PARAM_LT, PARAM_BLT, PARAM_TBGC, PARAM_TDSF, PARAM_DLA, PARAM_WPFONTSIZE, PARAM_WPC, PARAM_WPTC, PARAM_WPBC, PARAM_WPBA, PARAM_WPSYM, PARAM_WPSIZE, PARAM_WPSYMS, PARAM_DI, PARAM_IS, PARAM_IA, PARAM_ICS, NUM_PARAMS };
489 *** 1) Add to trw_layer_params and enumeration
490 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
493 /****** END PARAMETERS ******/
495 static VikTrwLayer* trw_layer_new ( gint drawmode );
496 /* Layer Interface function definitions */
497 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
498 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
499 static void trw_layer_free ( VikTrwLayer *trwlayer );
500 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
501 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
502 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
503 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
504 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
505 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
506 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
507 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
508 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
509 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
510 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
511 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
512 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
513 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
514 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
515 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
516 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
517 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
518 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
519 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
520 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
521 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
522 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
523 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
524 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
525 /* End Layer Interface function definitions */
527 VikLayerInterface vik_trw_layer_interface = {
534 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
538 params_groups, /* params_groups */
539 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
543 (VikLayerFuncCreate) trw_layer_create,
544 (VikLayerFuncRealize) trw_layer_realize,
545 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
546 (VikLayerFuncFree) trw_layer_free,
548 (VikLayerFuncProperties) NULL,
549 (VikLayerFuncDraw) trw_layer_draw,
550 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
552 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
553 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
555 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
556 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
558 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
559 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
560 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
561 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
562 (VikLayerFuncLayerSelected) trw_layer_selected,
564 (VikLayerFuncMarshall) trw_layer_marshall,
565 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
567 (VikLayerFuncSetParam) trw_layer_set_param,
568 (VikLayerFuncGetParam) trw_layer_get_param,
570 (VikLayerFuncReadFileData) a_gpspoint_read_file,
571 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
573 (VikLayerFuncDeleteItem) trw_layer_del_item,
574 (VikLayerFuncCutItem) trw_layer_cut_item,
575 (VikLayerFuncCopyItem) trw_layer_copy_item,
576 (VikLayerFuncPasteItem) trw_layer_paste_item,
577 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
579 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
581 (VikLayerFuncSelectClick) trw_layer_select_click,
582 (VikLayerFuncSelectMove) trw_layer_select_move,
583 (VikLayerFuncSelectRelease) trw_layer_select_release,
584 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
593 GType vik_trw_layer_get_type ()
595 static GType vtl_type = 0;
599 static const GTypeInfo vtl_info =
601 sizeof (VikTrwLayerClass),
602 NULL, /* base_init */
603 NULL, /* base_finalize */
604 NULL, /* class init */
605 NULL, /* class_finalize */
606 NULL, /* class_data */
607 sizeof (VikTrwLayer),
609 NULL /* instance init */
611 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
617 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
619 static gpointer pass_along[6];
625 pass_along[1] = NULL;
626 pass_along[2] = GINT_TO_POINTER (subtype);
627 pass_along[3] = sublayer;
628 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
629 pass_along[5] = NULL;
631 trw_layer_delete_item ( pass_along );
634 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
636 static gpointer pass_along[6];
642 pass_along[1] = NULL;
643 pass_along[2] = GINT_TO_POINTER (subtype);
644 pass_along[3] = sublayer;
645 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
646 pass_along[5] = NULL;
648 trw_layer_copy_item_cb(pass_along);
649 trw_layer_cut_item_cb(pass_along);
652 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
654 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
655 gint subtype = GPOINTER_TO_INT (pass_along[2]);
656 gpointer * sublayer = pass_along[3];
660 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
664 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
665 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
666 if ( wp && wp->name )
669 name = NULL; // Broken :(
672 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
673 if ( trk && trk->name )
676 name = NULL; // Broken :(
679 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
680 subtype, len, name, data);
684 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
686 trw_layer_copy_item_cb(pass_along);
687 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
688 trw_layer_delete_item(pass_along);
691 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
702 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
704 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
705 // 'Simple' memory copy of byte array from the marshalling above
706 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
707 fi = g_malloc ( *len );
709 memcpy(fi->data, id, il);
712 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
713 // less magic than before...
714 *len = sizeof(FlatItem) + 1 + il;
715 fi = g_malloc ( *len );
717 memcpy(fi->data, id, il);
721 *item = (guint8 *)fi;
724 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
726 FlatItem *fi = (FlatItem *) item;
728 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
733 w = vik_waypoint_unmarshall(fi->data, fi->len);
734 // When copying - we'll create a new name based on the original
735 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
736 vik_trw_layer_add_waypoint ( vtl, name, w );
737 waypoint_convert (NULL, w, &vtl->coord_mode);
739 // Consider if redraw necessary for the new item
740 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
741 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
744 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
749 t = vik_track_unmarshall(fi->data, fi->len);
750 // When copying - we'll create a new name based on the original
751 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
752 vik_trw_layer_add_track ( vtl, name, t );
753 track_convert (name, t, &vtl->coord_mode);
755 // Consider if redraw necessary for the new item
756 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
757 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
763 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
770 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
774 case PARAM_TV: vtl->tracks_visible = data.b; break;
775 case PARAM_WV: vtl->waypoints_visible = data.b; break;
776 case PARAM_DM: vtl->drawmode = data.u; break;
777 case PARAM_DP: vtl->drawpoints = data.b; break;
778 case PARAM_DE: vtl->drawelevation = data.b; break;
779 case PARAM_DS: vtl->drawstops = data.b; break;
780 case PARAM_DL: vtl->drawlines = data.b; break;
781 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
782 vtl->stop_length = data.u;
784 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
785 vtl->elevation_factor = data.u;
787 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
789 vtl->line_thickness = data.u;
790 trw_layer_new_track_gcs ( vtl, vp );
793 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
795 vtl->bg_line_thickness = data.u;
796 trw_layer_new_track_gcs ( vtl, vp );
799 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
800 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
801 case PARAM_DLA: vtl->drawlabels = data.b; break;
802 case PARAM_DI: vtl->drawimages = data.b; break;
803 case PARAM_IS: if ( data.u != vtl->image_size )
805 vtl->image_size = data.u;
806 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
807 g_queue_free ( vtl->image_cache );
808 vtl->image_cache = g_queue_new ();
811 case PARAM_IA: vtl->image_alpha = data.u; break;
812 case PARAM_ICS: vtl->image_cache_size = data.u;
813 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
814 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
816 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
817 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
818 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
819 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
820 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
821 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
822 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
823 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
828 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
830 VikLayerParamData rv;
833 case PARAM_TV: rv.b = vtl->tracks_visible; break;
834 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
835 case PARAM_DM: rv.u = vtl->drawmode; break;
836 case PARAM_DP: rv.b = vtl->drawpoints; break;
837 case PARAM_DE: rv.b = vtl->drawelevation; break;
838 case PARAM_EF: rv.u = vtl->elevation_factor; break;
839 case PARAM_DS: rv.b = vtl->drawstops; break;
840 case PARAM_SL: rv.u = vtl->stop_length; break;
841 case PARAM_DL: rv.b = vtl->drawlines; break;
842 case PARAM_LT: rv.u = vtl->line_thickness; break;
843 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
844 case PARAM_DLA: rv.b = vtl->drawlabels; break;
845 case PARAM_DI: rv.b = vtl->drawimages; break;
846 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
847 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
848 case PARAM_IS: rv.u = vtl->image_size; break;
849 case PARAM_IA: rv.u = vtl->image_alpha; break;
850 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
851 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
852 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
853 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
854 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
855 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
856 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
857 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
858 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
863 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
874 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
875 a_gpx_write_file(vtl, f, NULL);
876 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
879 g_file_get_contents(tmpname, &dd, &dl, NULL);
880 *len = sizeof(pl) + pl + dl;
881 *data = g_malloc(*len);
882 memcpy(*data, &pl, sizeof(pl));
883 memcpy(*data + sizeof(pl), pd, pl);
884 memcpy(*data + sizeof(pl) + pl, dd, dl);
893 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
895 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
901 memcpy(&pl, data, sizeof(pl));
903 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
906 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
907 g_critical("couldn't open temp file");
910 fwrite(data, len - pl - sizeof(pl), 1, f);
912 a_gpx_read_file(rv, f);
920 static GList * str_array_to_glist(gchar* data[])
924 for (p = (gpointer)data; *p; p++)
925 gl = g_list_prepend(gl, *p);
926 return(g_list_reverse(gl));
929 // Keep interesting hash function at least visible
931 static guint strcase_hash(gconstpointer v)
933 // 31 bit hash function
936 gchar s[128]; // malloc is too slow for reading big files
939 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
940 p[i] = toupper(t[i]);
946 for (p += 1; *p != '\0'; p++)
947 h = (h << 5) - h + *p;
954 static VikTrwLayer* trw_layer_new ( gint drawmode )
956 if (trw_layer_params[PARAM_DM].widget_data == NULL)
957 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
958 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
959 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
961 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
962 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
964 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
965 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
967 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
968 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
969 // and with normal PC processing capabilities - it has negligibile performance impact
970 // This also minimized the amount of rework - as the management of the hash tables already exists.
972 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
973 // we have to maintain a uniqueness (as before when multiple names where not allowed),
974 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
976 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
977 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
978 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
979 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
981 /* TODO: constants at top */
982 rv->waypoints_visible = rv->tracks_visible = TRUE;
983 rv->drawmode = drawmode;
984 rv->drawpoints = TRUE;
985 rv->drawstops = FALSE;
986 rv->drawelevation = FALSE;
987 rv->elevation_factor = 30;
988 rv->stop_length = 60;
989 rv->drawlines = TRUE;
990 rv->wplabellayout = NULL;
991 rv->wp_right_click_menu = NULL;
992 rv->track_right_click_menu = NULL;
993 rv->waypoint_gc = NULL;
994 rv->waypoint_text_gc = NULL;
995 rv->waypoint_bg_gc = NULL;
997 rv->track_draw_speed_factor = 30.0;
998 rv->line_thickness = 1;
999 rv->bg_line_thickness = 0;
1000 rv->current_wp = NULL;
1001 rv->current_wp_id = NULL;
1002 rv->current_track = NULL;
1003 rv->current_tpl = NULL;
1004 rv->current_tp_track = NULL;
1005 rv->current_tp_id = NULL;
1006 rv->moving_tp = FALSE;
1007 rv->moving_wp = FALSE;
1009 rv->draw_sync_done = TRUE;
1010 rv->draw_sync_do = TRUE;
1012 rv->route_finder_started = FALSE;
1013 rv->route_finder_check_added_track = FALSE;
1014 rv->route_finder_current_track = NULL;
1015 rv->route_finder_append = FALSE;
1017 rv->waypoint_rightclick = FALSE;
1019 rv->image_cache = g_queue_new();
1020 rv->image_size = 64;
1021 rv->image_alpha = 255;
1022 rv->image_cache_size = 300;
1023 rv->drawimages = TRUE;
1024 rv->drawlabels = TRUE;
1029 static void trw_layer_free ( VikTrwLayer *trwlayer )
1031 g_hash_table_destroy(trwlayer->waypoints);
1032 g_hash_table_destroy(trwlayer->tracks);
1034 /* ODC: replace with GArray */
1035 trw_layer_free_track_gcs ( trwlayer );
1037 if ( trwlayer->wp_right_click_menu )
1038 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1040 if ( trwlayer->track_right_click_menu )
1041 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1043 if ( trwlayer->wplabellayout != NULL)
1044 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1046 if ( trwlayer->waypoint_gc != NULL )
1047 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1049 if ( trwlayer->waypoint_text_gc != NULL )
1050 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1052 if ( trwlayer->waypoint_bg_gc != NULL )
1053 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1055 if ( trwlayer->tpwin != NULL )
1056 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1058 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1059 g_queue_free ( trwlayer->image_cache );
1062 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
1065 dp->xmpp = vik_viewport_get_xmpp ( vp );
1066 dp->ympp = vik_viewport_get_ympp ( vp );
1067 dp->width = vik_viewport_get_width ( vp );
1068 dp->height = vik_viewport_get_height ( vp );
1069 dp->center = vik_viewport_get_center ( vp );
1070 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1071 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1076 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1077 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1078 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1080 dp->ce1 = dp->center->east_west-w2;
1081 dp->ce2 = dp->center->east_west+w2;
1082 dp->cn1 = dp->center->north_south-h2;
1083 dp->cn2 = dp->center->north_south+h2;
1084 } else if ( dp->lat_lon ) {
1085 VikCoord upperleft, bottomright;
1086 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1087 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1088 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1089 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1090 dp->ce1 = upperleft.east_west;
1091 dp->ce2 = bottomright.east_west;
1092 dp->cn1 = bottomright.north_south;
1093 dp->cn2 = upperleft.north_south;
1096 dp->track_gc_iter = 0;
1100 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1101 * Here a simple traffic like light colour system is used:
1102 * . slow points are red
1103 * . average is yellow
1104 * . fast points are green
1106 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1109 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1110 if ( average_speed > 0 ) {
1111 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1112 if ( rv < low_speed )
1113 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1114 else if ( rv > high_speed )
1115 return VIK_TRW_LAYER_TRACK_GC_FAST;
1117 return VIK_TRW_LAYER_TRACK_GC_AVER;
1120 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1123 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1125 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1126 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1127 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1128 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1131 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1133 /* TODO: this function is a mess, get rid of any redundancy */
1134 GList *list = track->trackpoints;
1136 gboolean useoldvals = TRUE;
1138 gboolean drawpoints;
1140 gboolean drawelevation;
1141 gdouble min_alt, max_alt, alt_diff = 0;
1143 const guint8 tp_size_reg = 2;
1144 const guint8 tp_size_cur = 4;
1147 if ( dp->vtl->drawelevation )
1149 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1150 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1151 alt_diff = max_alt - min_alt;
1154 if ( ! track->visible )
1157 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1158 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1159 trw_layer_draw_track ( name, track, dp, TRUE );
1161 if ( drawing_white_background )
1162 drawpoints = drawstops = FALSE;
1164 drawpoints = dp->vtl->drawpoints;
1165 drawstops = dp->vtl->drawstops;
1168 gboolean drawing_highlight = FALSE;
1169 /* Current track - used for creation */
1170 if ( track == dp->vtl->current_track )
1171 main_gc = dp->vtl->current_track_gc;
1173 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1174 /* Draw all tracks of the layer in special colour */
1175 /* if track is member of selected layer or is the current selected track
1176 then draw in the highlight colour.
1177 NB this supercedes the drawmode */
1178 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1179 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1180 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1181 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1182 drawing_highlight = TRUE;
1185 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1186 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1188 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1192 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1193 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1195 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1200 int x, y, oldx, oldy;
1201 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1203 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1205 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1207 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1209 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1210 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1216 gdouble average_speed = 0.0;
1217 gdouble low_speed = 0.0;
1218 gdouble high_speed = 0.0;
1219 // If necessary calculate these values - which is done only once per track redraw
1220 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1221 // the percentage factor away from the average speed determines transistions between the levels
1222 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1223 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1224 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1227 while ((list = g_list_next(list)))
1229 tp = VIK_TRACKPOINT(list->data);
1230 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1232 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1233 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1234 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1235 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1236 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1238 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1241 * If points are the same in display coordinates, don't draw.
1243 if ( useoldvals && x == oldx && y == oldy )
1245 // Still need to process points to ensure 'stops' are drawn if required
1246 if ( drawstops && drawpoints && ! drawing_white_background && list->next &&
1247 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1248 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 );
1253 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1254 if ( drawpoints || dp->vtl->drawlines ) {
1255 // setup main_gc for both point and line drawing
1256 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1257 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1258 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1262 if ( drawpoints && ! drawing_white_background )
1267 * The concept of drawing stops is that a trackpoint
1268 * that is if the next trackpoint has a timestamp far into
1269 * the future, we draw a circle of 6x trackpoint size,
1270 * instead of a rectangle of 2x trackpoint size.
1271 * This is drawn first so the trackpoint will be drawn on top
1274 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1275 /* Stop point. Draw 6x circle. */
1276 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 );
1278 /* Regular point - draw 2x square. */
1279 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1282 /* Final point - draw 4x circle. */
1283 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 );
1286 if ((!tp->newsegment) && (dp->vtl->drawlines))
1289 /* UTM only: zone check */
1290 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1291 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1294 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1296 if ( drawing_white_background ) {
1297 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1301 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1302 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1304 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1309 tmp[1].y = oldy-FIXALTITUDE(list->data);
1311 tmp[2].y = y-FIXALTITUDE(list->next->data);
1316 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1317 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1319 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1320 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1322 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1333 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1335 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1336 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1338 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1340 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1341 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1342 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1346 * If points are the same in display coordinates, don't draw.
1348 if ( x != oldx || y != oldy )
1350 if ( drawing_white_background )
1351 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1353 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1359 * If points are the same in display coordinates, don't draw.
1361 if ( x != oldx && y != oldy )
1363 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1364 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1372 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1373 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
1374 dp->track_gc_iter = 0;
1377 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1378 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1380 trw_layer_draw_track ( name, track, dp, FALSE );
1383 static void cached_pixbuf_free ( CachedPixbuf *cp )
1385 g_object_unref ( G_OBJECT(cp->pixbuf) );
1386 g_free ( cp->image );
1389 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1391 return strcmp ( cp->image, name );
1394 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1397 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1398 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1399 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1402 GdkPixbuf *sym = NULL;
1403 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1405 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1407 if ( wp->image && dp->vtl->drawimages )
1409 GdkPixbuf *pixbuf = NULL;
1412 if ( dp->vtl->image_alpha == 0)
1415 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1417 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1420 gchar *image = wp->image;
1421 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1422 if ( ! regularthumb )
1424 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1425 image = "\x12\x00"; /* this shouldn't occur naturally. */
1429 CachedPixbuf *cp = NULL;
1430 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1431 if ( dp->vtl->image_size == 128 )
1432 cp->pixbuf = regularthumb;
1435 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1436 g_assert ( cp->pixbuf );
1437 g_object_unref ( G_OBJECT(regularthumb) );
1439 cp->image = g_strdup ( image );
1441 /* needed so 'click picture' tool knows how big the pic is; we don't
1442 * store it in cp because they may have been freed already. */
1443 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1444 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1446 g_queue_push_head ( dp->vtl->image_cache, cp );
1447 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1448 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1450 pixbuf = cp->pixbuf;
1454 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1460 w = gdk_pixbuf_get_width ( pixbuf );
1461 h = gdk_pixbuf_get_height ( pixbuf );
1463 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1465 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1466 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1467 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1468 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1469 // Highlighted - so draw a little border around the chosen one
1470 // single line seems a little weak so draw 2 of them
1471 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1472 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1473 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1474 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1477 if ( dp->vtl->image_alpha == 255 )
1478 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1480 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1482 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1486 /* DRAW ACTUAL DOT */
1487 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1488 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 );
1490 else if ( wp == dp->vtl->current_wp ) {
1491 switch ( dp->vtl->wp_symbol ) {
1492 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;
1493 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;
1494 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;
1495 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 );
1496 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 );
1500 switch ( dp->vtl->wp_symbol ) {
1501 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;
1502 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;
1503 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;
1504 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 );
1505 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;
1509 if ( dp->vtl->drawlabels )
1511 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1512 gint label_x, label_y;
1514 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1516 // Could this stored in the waypoint rather than recreating each pass?
1517 gchar *fsize = NULL;
1518 switch (dp->vtl->wp_font_size) {
1519 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1520 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1521 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1522 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1523 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1524 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1525 default: fsize = g_strdup ( "medium" ); break;
1528 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1530 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1531 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1533 // Fallback if parse failure
1534 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1536 g_free ( wp_label_markup );
1539 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1540 label_x = x - width/2;
1542 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1544 label_y = y - dp->vtl->wp_size - height - 2;
1546 /* if highlight mode on, then draw background text in highlight colour */
1547 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1548 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1549 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1550 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1551 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1553 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1556 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1558 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1563 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1565 static struct DrawingParams dp;
1566 g_assert ( l != NULL );
1568 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1571 if ( l->tracks_visible )
1572 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1574 if (l->waypoints_visible)
1575 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1578 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1581 if ( vtl->track_bg_gc )
1583 g_object_unref ( vtl->track_bg_gc );
1584 vtl->track_bg_gc = NULL;
1586 if ( vtl->current_track_gc )
1588 g_object_unref ( vtl->current_track_gc );
1589 vtl->current_track_gc = NULL;
1591 if ( vtl->current_track_newpoint_gc )
1593 g_object_unref ( vtl->current_track_newpoint_gc );
1594 vtl->current_track_newpoint_gc = NULL;
1597 if ( ! vtl->track_gc )
1599 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1600 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1601 g_array_free ( vtl->track_gc, TRUE );
1602 vtl->track_gc = NULL;
1605 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1607 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1608 gint width = vtl->line_thickness;
1610 if ( vtl->track_gc )
1611 trw_layer_free_track_gcs ( vtl );
1613 if ( vtl->track_bg_gc )
1614 g_object_unref ( vtl->track_bg_gc );
1615 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1617 // Ensure new track drawing heeds line thickness setting
1618 // however always have a minium of 2, as 1 pixel is really narrow
1619 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1621 if ( vtl->current_track_gc )
1622 g_object_unref ( vtl->current_track_gc );
1623 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1624 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1626 // 'newpoint' gc is exactly the same as the current track gc
1627 if ( vtl->current_track_newpoint_gc )
1628 g_object_unref ( vtl->current_track_newpoint_gc );
1629 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1630 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1632 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1634 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1636 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1637 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1638 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1639 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1640 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1641 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1642 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1643 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1644 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1645 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1647 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1649 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1651 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1652 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1653 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1655 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1658 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1660 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1661 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1663 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1664 /* early exit, as the rest is GUI related */
1668 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1669 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1671 trw_layer_new_track_gcs ( rv, vp );
1673 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1674 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1675 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1676 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1678 rv->has_verified_thumbnails = FALSE;
1679 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1681 rv->wp_draw_symbols = TRUE;
1682 rv->wp_font_size = FS_MEDIUM;
1684 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1686 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1691 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1693 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1695 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1696 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1698 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1701 *new_iter = *((GtkTreeIter *) pass_along[1]);
1702 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1704 if ( ! track->visible )
1705 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1708 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1710 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1711 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1712 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1714 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), NULL, TRUE, TRUE );
1717 *new_iter = *((GtkTreeIter *) pass_along[1]);
1718 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1720 if ( ! wp->visible )
1721 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1725 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1728 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1730 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1731 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1733 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1735 if ( ! vtl->tracks_visible )
1736 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1738 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1740 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1741 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1743 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1746 if ( ! vtl->waypoints_visible )
1747 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1749 pass_along[0] = &(vtl->waypoints_iter);
1750 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1752 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1756 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1760 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1761 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1762 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1764 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1766 return (t->visible ^= 1);
1770 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1772 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1774 return (t->visible ^= 1);
1783 * Return a property about tracks for this layer
1785 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1787 return vtl->line_thickness;
1790 // Structure to hold multiple track information for a layer
1799 * Build up layer multiple track information via updating the tooltip_tracks structure
1801 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1803 tt->length = tt->length + vik_track_get_length (tr);
1805 // Ensure times are available
1806 if ( tr->trackpoints &&
1807 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1808 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1811 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1812 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1814 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1815 // Hence initialize to the first 'proper' value
1816 if ( tt->start_time == 0 )
1817 tt->start_time = t1;
1818 if ( tt->end_time == 0 )
1821 // Update find the earliest / last times
1822 if ( t1 < tt->start_time )
1823 tt->start_time = t1;
1824 if ( t2 > tt->end_time )
1827 // Keep track of total time
1828 // there maybe gaps within a track (eg segments)
1829 // but this should be generally good enough for a simple indicator
1830 tt->duration = tt->duration + (int)(t2-t1);
1835 * Generate tooltip text for the layer.
1836 * This is relatively complicated as it considers information for
1837 * no tracks, a single track or multiple tracks
1838 * (which may or may not have timing information)
1840 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1851 static gchar tmp_buf[128];
1854 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1856 // Safety check - I think these should always be valid
1857 if ( vtl->tracks && vtl->waypoints ) {
1858 tooltip_tracks tt = { 0.0, 0, 0 };
1859 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1861 GDate* gdate_start = g_date_new ();
1862 g_date_set_time_t (gdate_start, tt.start_time);
1864 GDate* gdate_end = g_date_new ();
1865 g_date_set_time_t (gdate_end, tt.end_time);
1867 if ( g_date_compare (gdate_start, gdate_end) ) {
1868 // Dates differ so print range on separate line
1869 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1870 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1871 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1874 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1875 if ( tt.start_time != 0 )
1876 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1880 if ( tt.length > 0.0 ) {
1881 gdouble len_in_units;
1883 // Setup info dependent on distance units
1884 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1885 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1886 len_in_units = VIK_METERS_TO_MILES(tt.length);
1889 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1890 len_in_units = tt.length/1000.0;
1893 // Timing information if available
1895 if ( tt.duration > 0 ) {
1896 g_snprintf (tbuf1, sizeof(tbuf1),
1897 _(" in %d:%02d hrs:mins"),
1898 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1900 g_snprintf (tbuf2, sizeof(tbuf2),
1901 _("\n%sTotal Length %.1f %s%s"),
1902 tbuf3, len_in_units, tbuf4, tbuf1);
1905 // Put together all the elements to form compact tooltip text
1906 g_snprintf (tmp_buf, sizeof(tmp_buf),
1907 _("Tracks: %d - Waypoints: %d%s"),
1908 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1910 g_date_free (gdate_start);
1911 g_date_free (gdate_end);
1918 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1922 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1923 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1924 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1926 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1928 // Could be a better way of handling strings - but this works...
1929 gchar time_buf1[20];
1930 gchar time_buf2[20];
1931 time_buf1[0] = '\0';
1932 time_buf2[0] = '\0';
1933 static gchar tmp_buf[100];
1934 // Compact info: Short date eg (11/20/99), duration and length
1935 // Hopefully these are the things that are most useful and so promoted into the tooltip
1936 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1937 // %x The preferred date representation for the current locale without the time.
1938 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1939 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1940 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1942 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1945 // Get length and consider the appropriate distance units
1946 gdouble tr_len = vik_track_get_length(tr);
1947 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1948 switch (dist_units) {
1949 case VIK_UNITS_DISTANCE_KILOMETRES:
1950 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1952 case VIK_UNITS_DISTANCE_MILES:
1953 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1962 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1964 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1965 // NB It's OK to return NULL
1976 * Function to show basic track point information on the statusbar
1978 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1981 switch (a_vik_get_units_height ()) {
1982 case VIK_UNITS_HEIGHT_FEET:
1983 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1986 //VIK_UNITS_HEIGHT_METRES:
1987 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1992 if ( trkpt->has_timestamp ) {
1993 // Compact date time format
1994 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1998 // Position is put later on, as this bit may not be seen if the display is not big enough,
1999 // one can easily use the current pointer position to see this if needed
2000 gchar *lat = NULL, *lon = NULL;
2001 static struct LatLon ll;
2002 vik_coord_to_latlon (&(trkpt->coord), &ll);
2003 a_coords_latlon_to_string ( &ll, &lat, &lon );
2006 // Again is put later on, as this bit may not be seen if the display is not big enough
2007 // trackname can be seen from the treeview (when enabled)
2008 // Also name could be very long to not leave room for anything else
2011 if ( vtl->current_tp_track ) {
2012 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2015 // Combine parts to make overall message
2016 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2017 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2024 * Function to show basic waypoint information on the statusbar
2026 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2029 switch (a_vik_get_units_height ()) {
2030 case VIK_UNITS_HEIGHT_FEET:
2031 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2034 //VIK_UNITS_HEIGHT_METRES:
2035 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2039 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2040 // one can easily use the current pointer position to see this if needed
2041 gchar *lat = NULL, *lon = NULL;
2042 static struct LatLon ll;
2043 vik_coord_to_latlon (&(wpt->coord), &ll);
2044 a_coords_latlon_to_string ( &ll, &lat, &lon );
2046 // Combine parts to make overall message
2049 // Add comment if available
2050 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2052 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2053 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2060 * General layer selection function, find out which bit is selected and take appropriate action
2062 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2065 l->current_wp = NULL;
2066 l->current_wp_id = NULL;
2067 trw_layer_cancel_current_tp ( l, FALSE );
2070 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2074 case VIK_TREEVIEW_TYPE_LAYER:
2076 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2077 /* Mark for redraw */
2082 case VIK_TREEVIEW_TYPE_SUBLAYER:
2086 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2088 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2089 /* Mark for redraw */
2093 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2095 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2096 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
2097 /* Mark for redraw */
2101 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2103 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2104 /* Mark for redraw */
2108 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2110 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2112 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
2113 // Show some waypoint info
2114 set_statusbar_msg_info_wpt ( l, wpt );
2115 /* Mark for redraw */
2122 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2131 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2136 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2141 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2143 return l->waypoints;
2147 * ATM use a case sensitive find
2148 * Finds the first one
2150 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2152 if ( wp && wp->name )
2153 if ( ! strcmp ( wp->name, name ) )
2159 * Get waypoint by name - not guaranteed to be unique
2160 * Finds the first one
2162 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2164 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2168 * ATM use a case sensitive find
2169 * Finds the first one
2171 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2173 if ( trk && trk->name )
2174 if ( ! strcmp ( trk->name, name ) )
2180 * Get track by name - not guaranteed to be unique
2181 * Finds the first one
2183 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2185 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2188 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2190 static VikCoord fixme;
2191 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2192 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2193 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2194 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2195 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2196 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2197 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2198 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2199 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2202 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2204 GList *tr = trk->trackpoints;
2205 static VikCoord fixme;
2209 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2210 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2211 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2212 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2213 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2214 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2215 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2216 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2217 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2222 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2224 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2225 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2227 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2228 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2229 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2230 maxmin[0].lat = wpt_maxmin[0].lat;
2233 maxmin[0].lat = trk_maxmin[0].lat;
2235 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2236 maxmin[0].lon = wpt_maxmin[0].lon;
2239 maxmin[0].lon = trk_maxmin[0].lon;
2241 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2242 maxmin[1].lat = wpt_maxmin[1].lat;
2245 maxmin[1].lat = trk_maxmin[1].lat;
2247 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2248 maxmin[1].lon = wpt_maxmin[1].lon;
2251 maxmin[1].lon = trk_maxmin[1].lon;
2255 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2257 /* 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... */
2258 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2259 trw_layer_find_maxmin (vtl, maxmin);
2260 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2264 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2265 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2270 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2273 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2274 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2276 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2279 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2281 /* First set the center [in case previously viewing from elsewhere] */
2282 /* Then loop through zoom levels until provided positions are in view */
2283 /* This method is not particularly fast - but should work well enough */
2284 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2286 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2287 vik_viewport_set_center_coord ( vvp, &coord );
2289 /* Convert into definite 'smallest' and 'largest' positions */
2290 struct LatLon minmin;
2291 if ( maxmin[0].lat < maxmin[1].lat )
2292 minmin.lat = maxmin[0].lat;
2294 minmin.lat = maxmin[1].lat;
2296 struct LatLon maxmax;
2297 if ( maxmin[0].lon > maxmin[1].lon )
2298 maxmax.lon = maxmin[0].lon;
2300 maxmax.lon = maxmin[1].lon;
2302 /* Never zoom in too far - generally not that useful, as too close ! */
2303 /* Always recalculate the 'best' zoom level */
2305 vik_viewport_set_zoom ( vvp, zoom );
2307 gdouble min_lat, max_lat, min_lon, max_lon;
2308 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2309 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2310 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2311 /* NB I think the logic used in this test to determine if the bounds is within view
2312 fails if track goes across 180 degrees longitude.
2313 Hopefully that situation is not too common...
2314 Mind you viking doesn't really do edge locations to well anyway */
2315 if ( min_lat < minmin.lat &&
2316 max_lat > minmin.lat &&
2317 min_lon < maxmax.lon &&
2318 max_lon > maxmax.lon )
2319 /* Found within zoom level */
2324 vik_viewport_set_zoom ( vvp, zoom );
2328 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2330 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2331 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2332 trw_layer_find_maxmin (vtl, maxmin);
2333 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2336 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2341 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2343 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])) ) ) {
2344 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2347 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2350 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2352 GtkWidget *file_selector;
2354 gboolean failed = FALSE;
2355 file_selector = gtk_file_chooser_dialog_new (title,
2357 GTK_FILE_CHOOSER_ACTION_SAVE,
2358 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2359 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2361 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2363 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2365 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2366 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2368 gtk_widget_hide ( file_selector );
2369 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2374 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2376 gtk_widget_hide ( file_selector );
2377 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2382 gtk_widget_destroy ( file_selector );
2384 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2387 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2389 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2392 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2394 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2397 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2399 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2400 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2401 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2402 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2404 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2406 g_free ( auto_save_name );
2409 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2411 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2412 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2413 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2414 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2416 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2418 g_free ( auto_save_name );
2422 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2425 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2427 gchar *name_used = NULL;
2430 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2431 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2433 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2437 gchar *quoted_file = g_shell_quote ( name_used );
2438 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2439 g_free ( quoted_file );
2440 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2442 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2443 g_error_free ( err );
2447 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2448 //g_remove ( name_used );
2449 // Perhaps should be deleted when the program ends?
2450 // For now leave it to the user to delete it / use system temp cleanup methods.
2451 g_free ( name_used );
2455 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2457 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2460 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2462 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2465 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2467 gpointer layer_and_vlp[2];
2468 layer_and_vlp[0] = pass_along[0];
2469 layer_and_vlp[1] = pass_along[1];
2470 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2472 if ( !trk || !trk->name )
2475 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2476 gchar *auto_save_name = g_strdup ( trk->name );
2477 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2478 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2480 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2482 g_free ( auto_save_name );
2486 VikWaypoint *wp; // input
2487 gpointer uuid; // output
2490 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2492 wpu_udata *user_data = udata;
2493 if ( wp == user_data->wp ) {
2494 user_data->uuid = id;
2500 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2502 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2503 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2504 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2506 GTK_RESPONSE_REJECT,
2508 GTK_RESPONSE_ACCEPT,
2511 GtkWidget *label, *entry;
2512 label = gtk_label_new(_("Waypoint Name:"));
2513 entry = gtk_entry_new();
2515 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2516 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2517 gtk_widget_show_all ( label );
2518 gtk_widget_show_all ( entry );
2520 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2522 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2524 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2525 // Find *first* wp with the given name
2526 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2529 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2532 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2533 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2535 // Find and select on the side panel
2540 // Hmmm, want key of it
2541 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2543 if ( wpf && udata.uuid ) {
2544 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2545 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2554 gtk_widget_destroy ( dia );
2557 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2559 gchar *default_name = highest_wp_number_get(vtl);
2560 VikWaypoint *wp = vik_waypoint_new();
2561 gchar *returned_name;
2563 wp->coord = *def_coord;
2565 // Attempt to auto set height if DEM data is available
2566 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2567 if ( elev != VIK_DEM_INVALID_ELEVATION )
2568 wp->altitude = (gdouble)elev;
2570 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2572 if ( returned_name )
2575 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2576 g_free (default_name);
2577 g_free (returned_name);
2580 g_free (default_name);
2581 vik_waypoint_free(wp);
2585 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2588 struct LatLon one_ll, two_ll;
2589 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2591 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2592 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2593 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2594 VikViewport *vvp = vik_window_viewport(vw);
2595 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2596 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2597 vik_coord_to_latlon(&one, &one_ll);
2598 vik_coord_to_latlon(&two, &two_ll);
2599 if (one_ll.lat > two_ll.lat) {
2600 maxmin[0].lat = one_ll.lat;
2601 maxmin[1].lat = two_ll.lat;
2604 maxmin[0].lat = two_ll.lat;
2605 maxmin[1].lat = one_ll.lat;
2607 if (one_ll.lon > two_ll.lon) {
2608 maxmin[0].lon = one_ll.lon;
2609 maxmin[1].lon = two_ll.lon;
2612 maxmin[0].lon = two_ll.lon;
2613 maxmin[1].lon = one_ll.lon;
2615 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2618 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2620 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2621 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2622 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2624 trw_layer_find_maxmin (vtl, maxmin);
2625 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2628 #ifdef VIK_CONFIG_GEOTAG
2629 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2631 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2633 // Update directly - not changing the mtime
2634 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2637 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2639 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2642 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2646 * Use code in separate file for this feature as reasonably complex
2648 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2650 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2651 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2652 // Unset so can be reverified later if necessary
2653 vtl->has_verified_thumbnails = FALSE;
2655 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2661 static void trw_layer_geotagging ( gpointer lav[2] )
2663 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2664 // Unset so can be reverified later if necessary
2665 vtl->has_verified_thumbnails = FALSE;
2667 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2674 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2677 * Acquire into this TRW Layer straight from GPS Device
2679 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2681 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2682 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2683 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2684 VikViewport *vvp = vik_window_viewport(vw);
2686 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2687 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2690 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
2692 * Acquire into this TRW Layer from Google Directions
2694 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2696 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2697 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2698 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2699 VikViewport *vvp = vik_window_viewport(vw);
2701 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2705 #ifdef VIK_CONFIG_OPENSTREETMAP
2707 * Acquire into this TRW Layer from OSM
2709 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2711 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2712 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2713 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2714 VikViewport *vvp = vik_window_viewport(vw);
2716 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2720 #ifdef VIK_CONFIG_GEOCACHES
2722 * Acquire into this TRW Layer from Geocaching.com
2724 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2726 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2727 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2728 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2729 VikViewport *vvp = vik_window_viewport(vw);
2731 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2735 #ifdef VIK_CONFIG_GEOTAG
2737 * Acquire into this TRW Layer from images
2739 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2741 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2742 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2743 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2744 VikViewport *vvp = vik_window_viewport(vw);
2746 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2747 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2749 // Reverify thumbnails as they may have changed
2750 vtl->has_verified_thumbnails = FALSE;
2751 trw_layer_verify_thumbnails ( vtl, NULL );
2755 static void trw_layer_gps_upload ( gpointer lav[2] )
2757 gpointer pass_along[6];
2758 pass_along[0] = lav[0];
2759 pass_along[1] = lav[1];
2760 pass_along[2] = NULL; // No track - operate on the layer
2761 pass_along[3] = NULL;
2762 pass_along[4] = NULL;
2763 pass_along[5] = NULL;
2765 trw_layer_gps_upload_any ( pass_along );
2769 * If pass_along[3] is defined that this will upload just that track
2771 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
2773 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2774 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
2776 // May not actually get a track here as pass_along[3] can be null
2777 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2779 gboolean on_track = track ? TRUE : FALSE;
2781 if (on_track && !track->visible) {
2782 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
2786 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
2787 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2788 GTK_DIALOG_DESTROY_WITH_PARENT,
2790 GTK_RESPONSE_ACCEPT,
2792 GTK_RESPONSE_REJECT,
2795 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2796 GtkWidget *response_w = NULL;
2797 #if GTK_CHECK_VERSION (2, 20, 0)
2798 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2802 gtk_widget_grab_focus ( response_w );
2804 gpointer dgs = datasource_gps_setup ( dialog, on_track );
2806 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2807 datasource_gps_clean_up ( dgs );
2808 gtk_widget_destroy ( dialog );
2812 // Get info from reused datasource dialog widgets
2813 gchar* protocol = datasource_gps_get_protocol ( dgs );
2814 gchar* port = datasource_gps_get_descriptor ( dgs );
2815 // NB don't free the above strings as they're references to values held elsewhere
2816 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
2817 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
2818 gboolean turn_off = datasource_gps_get_off ( dgs );
2820 gtk_widget_destroy ( dialog );
2822 // When called from the viewport - work the corresponding layerspanel:
2824 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
2827 // Apply settings to transfer to the GPS device
2834 vik_layers_panel_get_viewport (vlp),
2842 * Acquire into this TRW Layer from any GPS Babel supported file
2844 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
2846 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2847 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2848 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2849 VikViewport *vvp = vik_window_viewport(vw);
2851 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
2854 static void trw_layer_new_wp ( gpointer lav[2] )
2856 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2857 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2858 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2859 instead return true if you want to update. */
2860 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 )
2861 vik_layers_panel_emit_update ( vlp );
2864 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2866 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2867 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2869 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2870 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2871 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2872 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2873 vik_layers_panel_emit_update ( vlp );
2877 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2879 /* NB do not care if wp is visible or not */
2880 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2883 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2885 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2886 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2888 /* Only 1 waypoint - jump straight to it */
2889 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2890 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2891 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2893 /* If at least 2 waypoints - find center and then zoom to fit */
2894 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2896 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2897 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2898 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2901 vik_layers_panel_emit_update ( vlp );
2904 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2906 static gpointer pass_along[2];
2908 GtkWidget *export_submenu;
2909 pass_along[0] = vtl;
2910 pass_along[1] = vlp;
2912 item = gtk_menu_item_new();
2913 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2914 gtk_widget_show ( item );
2916 /* Now with icons */
2917 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2918 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2919 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2920 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2921 gtk_widget_show ( item );
2923 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2924 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2925 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2926 gtk_widget_show ( item );
2928 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2930 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2931 gtk_widget_show ( item );
2933 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2934 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2936 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2937 gtk_widget_show ( item );
2939 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2941 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2942 gtk_widget_show ( item );
2944 export_submenu = gtk_menu_new ();
2945 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2946 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2947 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2948 gtk_widget_show ( item );
2949 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2951 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2952 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2953 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2954 gtk_widget_show ( item );
2956 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2958 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2959 gtk_widget_show ( item );
2961 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2963 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2964 gtk_widget_show ( item );
2966 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2968 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2969 gtk_widget_show ( item );
2971 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
2972 item = gtk_menu_item_new_with_mnemonic ( external1 );
2973 g_free ( external1 );
2974 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
2975 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2976 gtk_widget_show ( item );
2978 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
2979 item = gtk_menu_item_new_with_mnemonic ( external2 );
2980 g_free ( external2 );
2981 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
2982 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2983 gtk_widget_show ( item );
2985 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2986 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2988 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2989 gtk_widget_show ( item );
2991 #ifdef VIK_CONFIG_GEONAMES
2992 GtkWidget *wikipedia_submenu = gtk_menu_new();
2993 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2994 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2995 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2996 gtk_widget_show(item);
2997 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2999 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3000 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3001 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3002 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3003 gtk_widget_show ( item );
3005 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3006 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3007 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3008 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3009 gtk_widget_show ( item );
3012 #ifdef VIK_CONFIG_GEOTAG
3013 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3015 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3016 gtk_widget_show ( item );
3019 GtkWidget *acquire_submenu = gtk_menu_new ();
3020 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3021 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3022 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3023 gtk_widget_show ( item );
3024 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3026 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3028 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3029 gtk_widget_show ( item );
3031 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
3032 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3034 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3035 gtk_widget_show ( item );
3038 #ifdef VIK_CONFIG_OPENSTREETMAP
3039 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3041 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3042 gtk_widget_show ( item );
3045 #ifdef VIK_CONFIG_GEOCACHES
3046 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3048 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3049 gtk_widget_show ( item );
3052 #ifdef VIK_CONFIG_GEOTAG
3053 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3055 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3056 gtk_widget_show ( item );
3059 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3061 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3062 gtk_widget_show ( item );
3064 GtkWidget *upload_submenu = gtk_menu_new ();
3065 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3067 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3068 gtk_widget_show ( item );
3069 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3071 #ifdef VIK_CONFIG_OPENSTREETMAP
3072 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3073 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3075 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3076 gtk_widget_show ( item );
3079 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3080 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3082 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3083 gtk_widget_show ( item );
3085 GtkWidget *delete_submenu = gtk_menu_new ();
3086 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3087 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3088 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3089 gtk_widget_show ( item );
3090 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3092 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3093 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3095 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3096 gtk_widget_show ( item );
3098 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3099 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3100 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3101 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3102 gtk_widget_show ( item );
3104 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3105 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3106 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3107 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3108 gtk_widget_show ( item );
3110 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3111 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3113 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3114 gtk_widget_show ( item );
3116 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3117 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3119 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3120 gtk_widget_show ( item );
3123 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3124 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3126 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3127 gtk_widget_show ( item );
3131 // Fake Waypoint UUIDs vi simple increasing integer
3132 static guint wp_uuid = 0;
3134 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3138 vik_waypoint_set_name (wp, name);
3140 if ( VIK_LAYER(vtl)->realized )
3142 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3144 // Visibility column always needed for waypoints
3145 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3146 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
3148 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
3150 // Actual setting of visibility dependent on the waypoint
3151 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3153 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3156 highest_wp_number_add_wp(vtl, name);
3157 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3161 // Fake Track UUIDs vi simple increasing integer
3162 static guint tr_uuid = 0;
3164 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3168 vik_track_set_name (t, name);
3170 if ( VIK_LAYER(vtl)->realized )
3172 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3173 // Visibility column always needed for tracks
3174 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3175 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 );
3177 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 );
3179 // Actual setting of visibility dependent on the track
3180 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3182 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3185 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3189 /* to be called whenever a track has been deleted or may have been changed. */
3190 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3192 if (vtl->current_tp_track == trk )
3193 trw_layer_cancel_current_tp ( vtl, FALSE );
3196 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3199 gchar *newname = g_strdup(name);
3200 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
3201 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
3202 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3204 newname = new_newname;
3210 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3212 // No more uniqueness of name forced when loading from a file
3213 // This now makes this function a little redunant as we just flow the parameters through
3214 vik_trw_layer_add_waypoint ( vtl, name, wp );
3217 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3219 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3220 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3221 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3222 vik_track_free ( tr );
3223 vtl->route_finder_append = FALSE; /* this means we have added it */
3226 // No more uniqueness of name forced when loading from a file
3227 vik_trw_layer_add_track ( vtl, name, tr );
3229 if ( vtl->route_finder_check_added_track ) {
3230 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3231 vtl->route_finder_added_track = tr;
3236 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3238 *l = g_list_append(*l, id);
3242 * Move an item from one TRW layer to another TRW layer
3244 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3246 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3247 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3249 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3251 VikTrack *trk2 = vik_track_copy ( trk );
3252 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3253 vik_trw_layer_delete_track ( vtl_src, trk );
3256 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3257 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3259 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3261 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3262 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3263 trw_layer_delete_waypoint ( vtl_src, wp );
3267 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3269 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3270 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3272 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3273 GList *items = NULL;
3276 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3277 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3279 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3280 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3285 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3286 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3288 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3295 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3296 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3301 VikTrack *trk; // input
3302 gpointer uuid; // output
3305 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3307 trku_udata *user_data = udata;
3308 if ( trk == user_data->trk ) {
3309 user_data->uuid = id;
3315 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3317 gboolean was_visible = FALSE;
3319 if ( trk && trk->name ) {
3321 if ( trk == vtl->current_track ) {
3322 vtl->current_track = NULL;
3323 vtl->current_tp_track = NULL;
3324 vtl->current_tp_id = NULL;
3325 vtl->moving_tp = FALSE;
3328 was_visible = trk->visible;
3330 if ( trk == vtl->route_finder_current_track )
3331 vtl->route_finder_current_track = NULL;
3333 if ( trk == vtl->route_finder_added_track )
3334 vtl->route_finder_added_track = NULL;
3340 // Hmmm, want key of it
3341 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3343 if ( trkf && udata.uuid ) {
3344 /* could be current_tp, so we have to check */
3345 trw_layer_cancel_tps_of_track ( vtl, trk );
3347 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3350 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3351 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3352 g_hash_table_remove ( vtl->tracks, udata.uuid );
3359 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3361 gboolean was_visible = FALSE;
3363 if ( wp && wp->name ) {
3365 if ( wp == vtl->current_wp ) {
3366 vtl->current_wp = NULL;
3367 vtl->current_wp_id = NULL;
3368 vtl->moving_wp = FALSE;
3371 was_visible = wp->visible;
3377 // Hmmm, want key of it
3378 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3380 if ( wpf && udata.uuid ) {
3381 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3384 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3385 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3387 highest_wp_number_remove_wp(vtl, wp->name);
3388 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3397 // Only for temporary use by trw_layer_delete_waypoint_by_name
3398 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3400 wpu_udata *user_data = udata;
3401 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3402 user_data->uuid = id;
3409 * Delete a waypoint by the given name
3410 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3411 * as there be multiple waypoints with the same name
3413 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3416 // Fake a waypoint with the given name
3417 udata.wp = vik_waypoint_new ();
3418 vik_waypoint_set_name (udata.wp, name);
3419 // Currently only the name is used in this waypoint find function
3422 // Hmmm, want key of it
3423 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3425 vik_waypoint_free (udata.wp);
3427 if ( wpf && udata.uuid )
3428 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3434 VikTrack *trk; // input
3435 gpointer uuid; // output
3438 // Only for temporary use by trw_layer_delete_track_by_name
3439 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3441 tpu_udata *user_data = udata;
3442 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3443 user_data->uuid = id;
3450 * Delete a track by the given name
3451 * NOTE: ATM this will delete the first encountered Track with the specified name
3452 * as there be multiple track with the same name
3454 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3457 // Fake a track with the given name
3458 udata.trk = vik_track_new ();
3459 vik_track_set_name (udata.trk, name);
3460 // Currently only the name is used in this waypoint find function
3463 // Hmmm, want key of it
3464 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3466 vik_track_free (udata.trk);
3468 if ( trkf && udata.uuid )
3469 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3474 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3476 vik_treeview_item_delete (vt, it );
3479 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3482 vtl->current_track = NULL;
3483 vtl->route_finder_current_track = NULL;
3484 vtl->route_finder_added_track = NULL;
3485 if (vtl->current_tp_track)
3486 trw_layer_cancel_current_tp(vtl, FALSE);
3488 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3489 g_hash_table_remove_all(vtl->tracks_iters);
3490 g_hash_table_remove_all(vtl->tracks);
3492 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3495 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3497 vtl->current_wp = NULL;
3498 vtl->current_wp_id = NULL;
3499 vtl->moving_wp = FALSE;
3501 highest_wp_number_reset(vtl);
3503 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3504 g_hash_table_remove_all(vtl->waypoints_iters);
3505 g_hash_table_remove_all(vtl->waypoints);
3507 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3510 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3512 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3513 // Get confirmation from the user
3514 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3515 _("Are you sure you want to delete all tracks in %s?"),
3516 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3517 vik_trw_layer_delete_all_tracks (vtl);
3520 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3522 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3523 // Get confirmation from the user
3524 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3525 _("Are you sure you want to delete all waypoints in %s?"),
3526 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3527 vik_trw_layer_delete_all_waypoints (vtl);
3530 static void trw_layer_delete_item ( gpointer pass_along[6] )
3532 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3533 gboolean was_visible = FALSE;
3534 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3536 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3537 if ( wp && wp->name ) {
3538 if ( GPOINTER_TO_INT ( pass_along[4]) )
3539 // Get confirmation from the user
3540 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3541 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3542 _("Are you sure you want to delete the waypoint \"%s\""),
3545 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3550 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3551 if ( trk && trk->name ) {
3552 if ( GPOINTER_TO_INT ( pass_along[4]) )
3553 // Get confirmation from the user
3554 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3555 _("Are you sure you want to delete the track \"%s\""),
3558 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3562 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3566 static void trw_layer_properties_item ( gpointer pass_along[7] )
3568 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3569 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3571 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3573 if ( wp && wp->name )
3575 gboolean updated = FALSE;
3576 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3578 if ( updated && VIK_LAYER(vtl)->visible )
3579 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3584 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3585 if ( tr && tr->name )
3587 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3589 pass_along[1], /* vlp */
3590 pass_along[5] ); /* vvp */
3596 Parameter 1 -> VikLayersPanel
3597 Parameter 2 -> VikLayer
3598 Parameter 3 -> VikViewport
3600 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3603 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3604 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3607 /* since vlp not set, vl & vvp should be valid instead! */
3609 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3610 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3615 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3617 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3618 if ( trps && trps->data )
3619 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3622 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3624 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3625 if ( track && track->trackpoints )
3627 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3629 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
3630 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3631 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3632 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3633 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3637 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3639 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3640 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3642 vtl->current_track = track;
3643 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3645 if ( track->trackpoints )
3646 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3649 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
3651 * extend a track using route finder
3653 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3655 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3656 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3657 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3659 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3660 vtl->route_finder_coord = last_coord;
3661 vtl->route_finder_current_track = track;
3662 vtl->route_finder_started = TRUE;
3664 if ( track->trackpoints )
3665 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3670 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3672 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3673 /* Also warn if overwrite old elevation data */
3674 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3676 vik_track_apply_dem_data ( track );
3679 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3681 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3684 trps = g_list_last(trps);
3685 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3688 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3690 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3693 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3696 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3698 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3701 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3704 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3706 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3709 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3713 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3715 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3717 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3718 if ( trk && trk->trackpoints )
3720 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3721 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
3722 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3723 if ( pass_along[1] )
3724 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3726 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3730 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3732 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3733 trw_layer_tpwin_init ( vtl );
3736 /*************************************
3737 * merge/split by time routines
3738 *************************************/
3740 /* called for each key in track hash table.
3741 * If the current track has the same time stamp type, add it to the result,
3742 * except the one pointed by "exclude".
3743 * set exclude to NULL if there is no exclude to check.
3744 * Note that the result is in reverse (for performance reasons).
3749 gboolean with_timestamps;
3751 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3753 twt_udata *user_data = udata;
3754 VikTrackpoint *p1, *p2;
3756 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3760 if (VIK_TRACK(value)->trackpoints) {
3761 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3762 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3764 if ( user_data->with_timestamps ) {
3765 if (!p1->has_timestamp || !p2->has_timestamp) {
3770 // Don't add tracks with timestamps when getting non timestamp tracks
3771 if (p1->has_timestamp || p2->has_timestamp) {
3777 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3780 /* called for each key in track hash table. if original track user_data[1] is close enough
3781 * to the passed one, add it to list in user_data[0]
3783 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
3786 VikTrackpoint *p1, *p2;
3787 VikTrack *trk = VIK_TRACK(value);
3789 GList **nearby_tracks = ((gpointer *)user_data)[0];
3790 GList *tpoints = ((gpointer *)user_data)[1];
3793 * detect reasons for not merging, and return
3794 * if no reason is found not to merge, then do it.
3797 // Exclude the original track from the compiled list
3798 if (trk->trackpoints == tpoints) {
3802 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
3803 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
3805 if (trk->trackpoints) {
3806 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
3807 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
3809 if (!p1->has_timestamp || !p2->has_timestamp) {
3810 //g_print("no timestamp\n");
3814 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3815 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
3816 if (! (abs(t1 - p2->timestamp) < threshold ||
3818 abs(p1->timestamp - t2) < threshold)
3825 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
3828 /* comparison function used to sort tracks; a and b are hash table keys */
3829 /* Not actively used - can be restored if needed
3830 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3832 GHashTable *tracks = user_data;
3835 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3836 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3838 if (t1 < t2) return -1;
3839 if (t1 > t2) return 1;
3844 /* comparison function used to sort trackpoints */
3845 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3847 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3849 if (t1 < t2) return -1;
3850 if (t1 > t2) return 1;
3855 * comparison function which can be used to sort tracks or waypoints by name
3857 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3859 const gchar* namea = (const gchar*) a;
3860 const gchar* nameb = (const gchar*) b;
3861 if ( namea == NULL || nameb == NULL)
3864 // Same sort method as used in the vik_treeview_*_alphabetize functions
3865 return strcmp ( namea, nameb );
3869 * Attempt to merge selected track with other tracks specified by the user
3870 * Tracks to merge with must be of the same 'type' as the selected track -
3871 * either all with timestamps, or all without timestamps
3873 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3875 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3876 GList *other_tracks = NULL;
3877 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3879 if ( !track->trackpoints )
3883 udata.result = &other_tracks;
3884 udata.exclude = track->trackpoints;
3885 // Allow merging with 'similar' time type time tracks
3886 // i.e. either those times, or those without
3887 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3889 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3890 other_tracks = g_list_reverse(other_tracks);
3892 if ( !other_tracks ) {
3893 if ( udata.with_timestamps )
3894 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3896 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3900 // Sort alphabetically for user presentation
3901 // Convert into list of names for usage with dialog function
3902 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3903 GList *other_tracks_names = NULL;
3904 GList *iter = g_list_first ( other_tracks );
3906 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
3907 iter = g_list_next ( iter );
3910 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
3912 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3913 other_tracks_names, TRUE,
3914 _("Merge with..."), _("Select track to merge with"));
3915 g_list_free(other_tracks);
3916 g_list_free(other_tracks_names);
3921 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3922 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
3924 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3925 merge_track->trackpoints = NULL;
3926 vik_trw_layer_delete_track (vtl, merge_track);
3927 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3930 /* TODO: free data before free merge_list */
3931 for (l = merge_list; l != NULL; l = g_list_next(l))
3933 g_list_free(merge_list);
3934 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3938 // c.f. trw_layer_sorted_track_id_by_name_list
3939 // but don't add the specified track to the list (normally current track)
3940 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
3942 twt_udata *user_data = udata;
3945 if (trk->trackpoints == user_data->exclude) {
3949 // Sort named list alphabetically
3950 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
3954 * Join - this allows combining 'routes' and 'tracks'
3955 * i.e. doesn't care about whether tracks have consistent timestamps
3956 * ATM can only append one track at a time to the currently selected track
3958 static void trw_layer_append_track ( gpointer pass_along[6] )
3961 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3962 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3964 GList *other_tracks_names = NULL;
3966 // Sort alphabetically for user presentation
3967 // Convert into list of names for usage with dialog function
3968 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3970 udata.result = &other_tracks_names;
3971 udata.exclude = trk->trackpoints;
3973 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
3975 // Note the limit to selecting one track only
3976 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
3977 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
3978 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3982 _("Select the track to append after the current track"));
3984 g_list_free(other_tracks_names);
3986 // It's a list, but shouldn't contain more than one other track!
3987 if ( append_list ) {
3989 for (l = append_list; l != NULL; l = g_list_next(l)) {
3990 // TODO: at present this uses the first track found by name,
3991 // which with potential multiple same named tracks may not be the one selected...
3992 VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data );
3993 if ( append_track ) {
3994 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
3995 append_track->trackpoints = NULL;
3996 vik_trw_layer_delete_track (vtl, append_track);
3999 for (l = append_list; l != NULL; l = g_list_next(l))
4001 g_list_free(append_list);
4002 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4006 /* merge by segments */
4007 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4009 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4010 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4011 guint segments = vik_track_merge_segments ( trk );
4012 // NB currently no need to redraw as segments not actually shown on the display
4013 // However inform the user of what happened:
4015 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4016 g_snprintf(str, 64, tmp_str, segments);
4017 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4020 /* merge by time routine */
4021 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4023 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4027 GList *tracks_with_timestamp = NULL;
4028 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4029 if (orig_trk->trackpoints &&
4030 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4031 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4036 udata.result = &tracks_with_timestamp;
4037 udata.exclude = orig_trk->trackpoints;
4038 udata.with_timestamps = TRUE;
4039 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4040 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4042 if (!tracks_with_timestamp) {
4043 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4046 g_list_free(tracks_with_timestamp);
4048 static guint threshold_in_minutes = 1;
4049 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4050 _("Merge Threshold..."),
4051 _("Merge when time between tracks less than:"),
4052 &threshold_in_minutes)) {
4056 // keep attempting to merge all tracks until no merges within the time specified is possible
4057 gboolean attempt_merge = TRUE;
4058 GList *nearby_tracks = NULL;
4060 static gpointer params[3];
4062 while ( attempt_merge ) {
4064 // Don't try again unless tracks have changed
4065 attempt_merge = FALSE;
4067 trps = orig_trk->trackpoints;
4071 if (nearby_tracks) {
4072 g_list_free(nearby_tracks);
4073 nearby_tracks = NULL;
4076 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4077 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4079 /* g_print("Original track times: %d and %d\n", t1, t2); */
4080 params[0] = &nearby_tracks;
4081 params[1] = (gpointer)trps;
4082 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4084 /* get a list of adjacent-in-time tracks */
4085 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4088 GList *l = nearby_tracks;
4091 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4092 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4094 t1 = get_first_trackpoint(l)->timestamp;
4095 t2 = get_last_trackpoint(l)->timestamp;
4096 #undef get_first_trackpoint
4097 #undef get_last_trackpoint
4098 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4101 /* remove trackpoints from merged track, delete track */
4102 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4103 VIK_TRACK(l->data)->trackpoints = NULL;
4104 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4106 // Tracks have changed, therefore retry again against all the remaining tracks
4107 attempt_merge = TRUE;
4112 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
4115 g_list_free(nearby_tracks);
4116 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4120 * Split a track at the currently selected trackpoint
4122 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl )
4124 if ( !vtl->current_tpl )
4127 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
4128 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4130 VikTrack *tr = vik_track_new ();
4131 GList *newglist = g_list_alloc ();
4132 newglist->prev = NULL;
4133 newglist->next = vtl->current_tpl->next;
4134 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4135 tr->trackpoints = newglist;
4137 vtl->current_tpl->next->prev = newglist; /* end old track here */
4138 vtl->current_tpl->next = NULL;
4140 vtl->current_tpl = newglist; /* change tp to first of new track. */
4141 vtl->current_tp_track = tr;
4145 vik_trw_layer_add_track ( vtl, name, tr );
4151 // Also need id of newly created track
4152 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4153 if ( trkf && udata.uuid )
4154 vtl->current_tp_id = udata.uuid;
4156 vtl->current_tp_id = NULL;
4158 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4163 /* split by time routine */
4164 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
4166 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4167 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4168 GList *trps = track->trackpoints;
4170 GList *newlists = NULL;
4171 GList *newtps = NULL;
4172 static guint thr = 1;
4179 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4180 _("Split Threshold..."),
4181 _("Split when time between trackpoints exceeds:"),
4186 /* iterate through trackpoints, and copy them into new lists without touching original list */
4187 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
4191 ts = VIK_TRACKPOINT(iter->data)->timestamp;
4193 g_print("panic: ts < prev_ts: this should never happen!\n");
4196 if (ts - prev_ts > thr*60) {
4197 /* flush accumulated trackpoints into new list */
4198 newlists = g_list_append(newlists, g_list_reverse(newtps));
4202 /* accumulate trackpoint copies in newtps, in reverse order */
4203 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4205 iter = g_list_next(iter);
4208 newlists = g_list_append(newlists, g_list_reverse(newtps));
4211 /* put lists of trackpoints into tracks */
4213 // Only bother updating if the split results in new tracks
4214 if (g_list_length (newlists) > 1) {
4219 tr = vik_track_new();
4220 tr->visible = track->visible;
4221 tr->trackpoints = (GList *)(iter->data);
4223 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4224 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4225 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
4226 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
4228 iter = g_list_next(iter);
4230 // Remove original track and then update the display
4231 vik_trw_layer_delete_track (vtl, track);
4232 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4234 g_list_free(newlists);
4238 * Split a track by the number of points as specified by the user
4240 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
4242 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4243 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4245 // Check valid track
4246 GList *trps = track->trackpoints;
4250 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4251 _("Split Every Nth Point"),
4252 _("Split on every Nth point:"),
4253 250, // Default value as per typical limited track capacity of various GPS devices
4257 // Was a valid number returned?
4263 GList *newlists = NULL;
4264 GList *newtps = NULL;
4269 /* accumulate trackpoint copies in newtps, in reverse order */
4270 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4272 if (count >= points) {
4273 /* flush accumulated trackpoints into new list */
4274 newlists = g_list_append(newlists, g_list_reverse(newtps));
4278 iter = g_list_next(iter);
4281 // If there is a remaining chunk put that into the new split list
4282 // This may well be the whole track if no split points were encountered
4284 newlists = g_list_append(newlists, g_list_reverse(newtps));
4287 /* put lists of trackpoints into tracks */
4289 // Only bother updating if the split results in new tracks
4290 if (g_list_length (newlists) > 1) {
4295 tr = vik_track_new();
4296 tr->visible = track->visible;
4297 tr->trackpoints = (GList *)(iter->data);
4299 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4300 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4302 iter = g_list_next(iter);
4304 // Remove original track and then update the display
4305 vik_trw_layer_delete_track (vtl, track);
4306 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4308 g_list_free(newlists);
4312 * Split a track at the currently selected trackpoint
4314 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
4316 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4317 trw_layer_split_at_selected_trackpoint ( vtl );
4321 * Split a track by its segments
4323 static void trw_layer_split_segments ( gpointer pass_along[6] )
4325 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4326 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4329 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
4332 for ( i = 0; i < ntracks; i++ ) {
4334 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
4335 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
4340 // Remove original track
4341 vik_trw_layer_delete_track ( vtl, trk );
4342 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4345 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
4348 /* end of split/merge routines */
4351 * Delete adjacent track points at the same position
4352 * AKA Delete Dulplicates on the Properties Window
4354 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
4356 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4357 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4359 gulong removed = vik_track_remove_dup_points ( trk );
4361 // Track has been updated so update tps:
4362 trw_layer_cancel_tps_of_track ( vtl, trk );
4364 // Inform user how much was deleted as it's not obvious from the normal view
4366 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4367 g_snprintf(str, 64, tmp_str, removed);
4368 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4370 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4374 * Delete adjacent track points with the same timestamp
4375 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
4377 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
4379 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4380 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4382 gulong removed = vik_track_remove_same_time_points ( trk );
4384 // Track has been updated so update tps:
4385 trw_layer_cancel_tps_of_track ( vtl, trk );
4387 // Inform user how much was deleted as it's not obvious from the normal view
4389 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4390 g_snprintf(str, 64, tmp_str, removed);
4391 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4393 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4399 static void trw_layer_reverse ( gpointer pass_along[6] )
4401 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4402 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4404 // Check valid track
4405 GList *trps = track->trackpoints;
4409 vik_track_reverse ( track );
4411 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4415 * Similar to trw_layer_enum_item, but this uses a sorted method
4418 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4420 GList **list = (GList**)udata;
4421 // *list = g_list_prepend(*all, key); //unsorted method
4422 // Sort named list alphabetically
4423 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4428 * Now Waypoint specific sort
4430 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4432 GList **list = (GList**)udata;
4433 // Sort named list alphabetically
4434 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4438 * Track specific sort
4440 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4442 GList **list = (GList**)udata;
4443 // Sort named list alphabetically
4444 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4449 gboolean has_same_track_name;
4450 const gchar *same_track_name;
4451 } same_track_name_udata;
4453 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4455 const gchar* namea = (const gchar*) aa;
4456 const gchar* nameb = (const gchar*) bb;
4459 gint result = strcmp ( namea, nameb );
4461 if ( result == 0 ) {
4462 // Found two names the same
4463 same_track_name_udata *user_data = udata;
4464 user_data->has_same_track_name = TRUE;
4465 user_data->same_track_name = namea;
4468 // Leave ordering the same
4473 * Find out if any tracks have the same name in this layer
4475 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4477 // Sort items by name, then compare if any next to each other are the same
4479 GList *track_names = NULL;
4480 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4483 if ( ! track_names )
4486 same_track_name_udata udata;
4487 udata.has_same_track_name = FALSE;
4489 // Use sort routine to traverse list comparing items
4490 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4491 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4492 // Still no tracks...
4496 return udata.has_same_track_name;
4500 * Force unqiue track names for this layer
4501 * Note the panel is a required parameter to enable the update of the names displayed
4503 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4505 // . Search list for an instance of repeated name
4506 // . get track of this name
4507 // . create new name
4508 // . rename track & update equiv. treeview iter
4509 // . repeat until all different
4511 same_track_name_udata udata;
4513 GList *track_names = NULL;
4514 udata.has_same_track_name = FALSE;
4515 udata.same_track_name = NULL;
4517 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4520 if ( ! track_names )
4523 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4525 // Still no tracks...
4526 if ( ! dummy_list1 )
4529 while ( udata.has_same_track_name ) {
4531 // Find a track with the same name
4532 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4536 g_critical("Houston, we've had a problem.");
4537 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4538 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4543 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4544 vik_track_set_name ( trk, newname );
4550 // Need want key of it for treeview update
4551 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4553 if ( trkf && udataU.uuid ) {
4555 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4558 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4559 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4560 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4565 // Start trying to find same names again...
4567 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4568 udata.has_same_track_name = FALSE;
4569 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4571 // No tracks any more - give up searching
4572 if ( ! dummy_list2 )
4573 udata.has_same_track_name = FALSE;
4577 vik_layers_panel_emit_update ( vlp );
4583 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4585 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4588 // Ensure list of track names offered is unique
4589 if ( trw_layer_has_same_track_names ( vtl ) ) {
4590 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4591 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4592 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4598 // Sort list alphabetically for better presentation
4599 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4602 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4606 // Get list of items to delete from the user
4607 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4610 _("Delete Selection"),
4611 _("Select tracks to delete"));
4614 // Delete requested tracks
4615 // since specificly requested, IMHO no need for extra confirmation
4616 if ( delete_list ) {
4618 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4619 // This deletes first trk it finds of that name (but uniqueness is enforced above)
4620 trw_layer_delete_track_by_name (vtl, l->data);
4622 g_list_free(delete_list);
4623 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4628 gboolean has_same_waypoint_name;
4629 const gchar *same_waypoint_name;
4630 } same_waypoint_name_udata;
4632 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4634 const gchar* namea = (const gchar*) aa;
4635 const gchar* nameb = (const gchar*) bb;
4638 gint result = strcmp ( namea, nameb );
4640 if ( result == 0 ) {
4641 // Found two names the same
4642 same_waypoint_name_udata *user_data = udata;
4643 user_data->has_same_waypoint_name = TRUE;
4644 user_data->same_waypoint_name = namea;
4647 // Leave ordering the same
4652 * Find out if any waypoints have the same name in this layer
4654 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
4656 // Sort items by name, then compare if any next to each other are the same
4658 GList *waypoint_names = NULL;
4659 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4662 if ( ! waypoint_names )
4665 same_waypoint_name_udata udata;
4666 udata.has_same_waypoint_name = FALSE;
4668 // Use sort routine to traverse list comparing items
4669 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4670 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4671 // Still no waypoints...
4675 return udata.has_same_waypoint_name;
4679 * Force unqiue waypoint names for this layer
4680 * Note the panel is a required parameter to enable the update of the names displayed
4682 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4684 // . Search list for an instance of repeated name
4685 // . get waypoint of this name
4686 // . create new name
4687 // . rename waypoint & update equiv. treeview iter
4688 // . repeat until all different
4690 same_waypoint_name_udata udata;
4692 GList *waypoint_names = NULL;
4693 udata.has_same_waypoint_name = FALSE;
4694 udata.same_waypoint_name = NULL;
4696 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4699 if ( ! waypoint_names )
4702 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4704 // Still no waypoints...
4705 if ( ! dummy_list1 )
4708 while ( udata.has_same_waypoint_name ) {
4710 // Find a waypoint with the same name
4711 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
4715 g_critical("Houston, we've had a problem.");
4716 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4717 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
4722 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
4723 vik_waypoint_set_name ( waypoint, newname );
4726 udataU.wp = waypoint;
4729 // Need want key of it for treeview update
4730 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4732 if ( wpf && udataU.uuid ) {
4734 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4737 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4738 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4739 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4744 // Start trying to find same names again...
4745 waypoint_names = NULL;
4746 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4747 udata.has_same_waypoint_name = FALSE;
4748 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4750 // No waypoints any more - give up searching
4751 if ( ! dummy_list2 )
4752 udata.has_same_waypoint_name = FALSE;
4756 vik_layers_panel_emit_update ( vlp );
4762 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4764 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4767 // Ensure list of waypoint names offered is unique
4768 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
4769 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4770 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4771 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4777 // Sort list alphabetically for better presentation
4778 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4780 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4784 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4786 // Get list of items to delete from the user
4787 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4790 _("Delete Selection"),
4791 _("Select waypoints to delete"));
4794 // Delete requested waypoints
4795 // since specificly requested, IMHO no need for extra confirmation
4796 if ( delete_list ) {
4798 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4799 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
4800 trw_layer_delete_waypoint_by_name (vtl, l->data);
4802 g_list_free(delete_list);
4803 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4808 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4810 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4812 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4815 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4817 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4818 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4822 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4824 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4826 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
4828 // No actual change to the name supplied
4829 if (strcmp(newname, wp->name) == 0 )
4832 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4835 // An existing waypoint has been found with the requested name
4836 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4837 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
4842 // Update WP name and refresh the treeview
4843 vik_waypoint_set_name (wp, newname);
4845 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4846 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4849 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4854 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4856 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
4858 // No actual change to the name supplied
4859 if (strcmp(newname, trk->name) == 0)
4862 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4865 // An existing track has been found with the requested name
4866 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4867 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4871 // Update track name and refresh GUI parts
4872 vik_track_set_name (trk, newname);
4874 // Update any subwindows that could be displaying this track which has changed name
4875 // Only one Track Edit Window
4876 if ( l->current_tp_track == trk && l->tpwin ) {
4877 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
4879 // Property Dialog of the track
4880 vik_trw_layer_propwin_update ( trk );
4882 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4883 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4886 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4893 static gboolean is_valid_geocache_name ( gchar *str )
4895 gint len = strlen ( str );
4896 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]));
4899 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
4901 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4902 a_acquire_set_filter_track ( trk );
4905 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
4907 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
4908 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
4911 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
4913 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4915 gchar *escaped = uri_escape ( tr->comment );
4916 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4917 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4923 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4924 /* viewpoint is now available instead */
4925 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4927 static gpointer pass_along[8];
4929 gboolean rv = FALSE;
4932 pass_along[1] = vlp;
4933 pass_along[2] = GINT_TO_POINTER (subtype);
4934 pass_along[3] = sublayer;
4935 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
4936 pass_along[5] = vvp;
4937 pass_along[6] = iter;
4938 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
4940 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4944 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
4945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
4946 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4947 gtk_widget_show ( item );
4949 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4950 VikTrwLayer *vtl = l;
4951 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
4952 if (tr && tr->property_dialog)
4953 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
4956 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
4957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
4958 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4959 gtk_widget_show ( item );
4961 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
4962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
4963 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4964 gtk_widget_show ( item );
4966 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
4967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
4968 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4969 gtk_widget_show ( item );
4971 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4973 gboolean separator_created = FALSE;
4975 /* could be a right-click using the tool */
4976 if ( vlp != NULL ) {
4977 item = gtk_menu_item_new ();
4978 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4979 gtk_widget_show ( item );
4981 separator_created = TRUE;
4983 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4984 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4986 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4987 gtk_widget_show ( item );
4990 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4992 if ( wp && wp->name ) {
4993 if ( is_valid_geocache_name ( wp->name ) ) {
4995 if ( !separator_created ) {
4996 item = gtk_menu_item_new ();
4997 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4998 gtk_widget_show ( item );
4999 separator_created = TRUE;
5002 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
5003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
5004 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5005 gtk_widget_show ( item );
5009 if ( wp && wp->image )
5011 if ( !separator_created ) {
5012 item = gtk_menu_item_new ();
5013 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5014 gtk_widget_show ( item );
5015 separator_created = TRUE;
5018 // Set up image paramater
5019 pass_along[5] = wp->image;
5021 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
5022 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
5023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
5024 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5025 gtk_widget_show ( item );
5027 #ifdef VIK_CONFIG_GEOTAG
5028 GtkWidget *geotag_submenu = gtk_menu_new ();
5029 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
5030 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
5031 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5032 gtk_widget_show ( item );
5033 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
5035 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
5036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
5037 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5038 gtk_widget_show ( item );
5040 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
5041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
5042 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5043 gtk_widget_show ( item );
5050 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
5053 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
5054 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5055 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
5056 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5057 gtk_widget_show ( item );
5060 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
5062 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
5063 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
5065 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5066 gtk_widget_show ( item );
5068 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
5069 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
5071 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5072 gtk_widget_show ( item );
5074 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
5075 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
5077 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5078 gtk_widget_show ( item );
5080 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
5081 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
5083 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5084 gtk_widget_show ( item );
5087 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
5091 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
5092 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
5094 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5095 gtk_widget_show ( item );
5097 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
5098 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
5100 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5101 gtk_widget_show ( item );
5103 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
5104 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
5106 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5107 gtk_widget_show ( item );
5110 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5112 item = gtk_menu_item_new ();
5113 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5114 gtk_widget_show ( item );
5116 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
5117 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5118 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
5119 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5120 gtk_widget_show ( item );
5122 GtkWidget *goto_submenu;
5123 goto_submenu = gtk_menu_new ();
5124 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5125 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5126 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5127 gtk_widget_show ( item );
5128 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
5130 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
5131 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
5132 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
5133 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5134 gtk_widget_show ( item );
5136 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
5137 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
5139 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5140 gtk_widget_show ( item );
5142 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
5143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
5144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
5145 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5146 gtk_widget_show ( item );
5148 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
5149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
5150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
5151 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5152 gtk_widget_show ( item );
5154 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
5155 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
5156 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
5157 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5158 gtk_widget_show ( item );
5160 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
5161 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
5162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
5163 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5164 gtk_widget_show ( item );
5166 GtkWidget *combine_submenu;
5167 combine_submenu = gtk_menu_new ();
5168 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
5169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
5170 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5171 gtk_widget_show ( item );
5172 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
5174 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
5175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
5176 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5177 gtk_widget_show ( item );
5179 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
5180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
5181 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5182 gtk_widget_show ( item );
5184 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
5185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
5186 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5187 gtk_widget_show ( item );
5189 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
5190 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
5191 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5192 gtk_widget_show ( item );
5194 GtkWidget *split_submenu;
5195 split_submenu = gtk_menu_new ();
5196 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
5197 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
5198 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5199 gtk_widget_show ( item );
5200 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
5202 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
5203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
5204 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5205 gtk_widget_show ( item );
5207 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
5208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
5209 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5210 gtk_widget_show ( item );
5212 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
5213 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
5214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
5215 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5216 gtk_widget_show ( item );
5218 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
5219 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
5220 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5221 gtk_widget_show ( item );
5222 // Make it available only when a trackpoint is selected.
5223 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
5225 GtkWidget *delete_submenu;
5226 delete_submenu = gtk_menu_new ();
5227 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
5228 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
5229 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5230 gtk_widget_show ( item );
5231 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
5233 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
5234 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
5235 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5236 gtk_widget_show ( item );
5238 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
5239 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
5240 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5241 gtk_widget_show ( item );
5243 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
5244 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
5245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
5246 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5247 gtk_widget_show ( item );
5249 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
5251 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
5252 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
5253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
5254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5255 gtk_widget_show ( item );
5258 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
5259 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
5260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
5261 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5262 gtk_widget_show ( item );
5264 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
5265 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
5266 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
5267 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5268 gtk_widget_show ( item );
5270 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
5271 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
5272 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
5273 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5274 gtk_widget_show ( item );
5276 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
5277 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
5278 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
5279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
5280 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5281 gtk_widget_show ( item );
5284 GtkWidget *upload_submenu = gtk_menu_new ();
5285 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
5286 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5287 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5288 gtk_widget_show ( item );
5289 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
5291 #ifdef VIK_CONFIG_OPENSTREETMAP
5292 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
5293 // Convert internal pointer into actual track for usage outside this file
5294 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
5295 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
5297 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5298 gtk_widget_show ( item );
5301 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
5302 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
5303 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
5304 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5305 gtk_widget_show ( item );
5307 if ( is_valid_google_route ( l, sublayer ) )
5309 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
5310 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
5311 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
5312 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5313 gtk_widget_show ( item );
5316 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
5317 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5318 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
5319 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5320 gtk_widget_show ( item );
5322 /* ATM This function is only available via the layers panel, due to needing a vlp */
5324 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
5325 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
5326 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
5328 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5329 gtk_widget_show ( item );
5333 #ifdef VIK_CONFIG_GEOTAG
5334 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
5335 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
5336 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5337 gtk_widget_show ( item );
5340 // Only show on viewport popmenu when a trackpoint is selected
5341 if ( ! vlp && l->current_tpl ) {
5343 item = gtk_menu_item_new ();
5344 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5345 gtk_widget_show ( item );
5347 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
5348 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
5349 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
5350 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5351 gtk_widget_show ( item );
5359 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
5362 if (!vtl->current_tpl)
5364 if (!vtl->current_tpl->next)
5367 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
5368 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
5370 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
5373 VikTrackpoint *tp_new = vik_trackpoint_new();
5374 struct LatLon ll_current, ll_next;
5375 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
5376 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
5378 /* main positional interpolation */
5379 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
5380 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
5382 /* Now other properties that can be interpolated */
5383 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
5385 if (tp_current->has_timestamp && tp_next->has_timestamp) {
5386 /* Note here the division is applied to each part, then added
5387 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
5388 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
5389 tp_new->has_timestamp = TRUE;
5392 if (tp_current->speed != NAN && tp_next->speed != NAN)
5393 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
5395 /* TODO - improve interpolation of course, as it may not be correct.
5396 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
5397 [similar applies if value is in radians] */
5398 if (tp_current->course != NAN && tp_next->course != NAN)
5399 tp_new->speed = (tp_current->course + tp_next->course)/2;
5401 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
5403 /* Insert new point into the trackpoints list after the current TP */
5404 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5405 gint index = g_list_index ( tr->trackpoints, tp_current );
5407 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
5412 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
5418 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
5422 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
5424 if ( vtl->current_tpl )
5426 vtl->current_tpl = NULL;
5427 vtl->current_tp_track = NULL;
5428 vtl->current_tp_id = NULL;
5429 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5433 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
5435 g_assert ( vtl->tpwin != NULL );
5436 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
5437 trw_layer_cancel_current_tp ( vtl, TRUE );
5439 if ( vtl->current_tpl == NULL )
5442 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
5444 trw_layer_split_at_selected_trackpoint ( vtl );
5445 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5447 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5449 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5455 // Find available adjacent trackpoint
5456 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5458 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5459 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5461 // Delete current trackpoint
5462 vik_trackpoint_free ( vtl->current_tpl->data );
5463 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5465 // Set to current to the available adjacent trackpoint
5466 vtl->current_tpl = new_tpl;
5468 // Reset dialog with the available adjacent trackpoint
5469 if ( vtl->current_tp_track )
5470 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
5472 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5476 // Delete current trackpoint
5477 vik_trackpoint_free ( vtl->current_tpl->data );
5478 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5479 trw_layer_cancel_current_tp ( vtl, FALSE );
5482 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5484 if ( vtl->current_tp_track )
5485 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
5486 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
5488 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5490 if ( vtl->current_tp_track )
5491 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
5492 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5494 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5496 trw_layer_insert_tp_after_current_tp ( vtl );
5497 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5499 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
5500 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5503 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5507 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5508 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5509 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5510 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5511 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5513 if ( vtl->current_tpl )
5514 if ( vtl->current_tp_track )
5515 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5516 /* set layer name and TP data */
5519 /***************************************************************************
5521 ***************************************************************************/
5523 /*** Utility data structures and functions ****/
5527 gint closest_x, closest_y;
5528 gpointer *closest_wp_id;
5529 VikWaypoint *closest_wp;
5535 gint closest_x, closest_y;
5536 gpointer closest_track_id;
5537 VikTrackpoint *closest_tp;
5542 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5548 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5550 // If waypoint has an image then use the image size to select
5552 gint slackx, slacky;
5553 slackx = wp->image_width / 2;
5554 slacky = wp->image_height / 2;
5556 if ( x <= params->x + slackx && x >= params->x - slackx
5557 && y <= params->y + slacky && y >= params->y - slacky ) {
5558 params->closest_wp_id = id;
5559 params->closest_wp = wp;
5560 params->closest_x = x;
5561 params->closest_y = y;
5564 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5565 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5566 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5568 params->closest_wp_id = id;
5569 params->closest_wp = wp;
5570 params->closest_x = x;
5571 params->closest_y = y;
5575 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5577 GList *tpl = t->trackpoints;
5586 tp = VIK_TRACKPOINT(tpl->data);
5588 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5590 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5591 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5592 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5594 params->closest_track_id = id;
5595 params->closest_tp = tp;
5596 params->closest_tpl = tpl;
5597 params->closest_x = x;
5598 params->closest_y = y;
5604 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5606 TPSearchParams params;
5610 params.closest_track_id = NULL;
5611 params.closest_tp = NULL;
5612 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5613 return params.closest_tp;
5616 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5618 WPSearchParams params;
5622 params.closest_wp = NULL;
5623 params.closest_wp_id = NULL;
5624 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5625 return params.closest_wp;
5629 // Some forward declarations
5630 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5631 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5632 static void marker_end_move ( tool_ed_t *t );
5635 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5639 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5641 // Here always allow snapping back to the original location
5642 // this is useful when one decides not to move the thing afterall
5643 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5646 if ( event->state & GDK_CONTROL_MASK )
5648 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5650 new_coord = tp->coord;
5654 if ( event->state & GDK_SHIFT_MASK )
5656 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5658 new_coord = wp->coord;
5662 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5664 marker_moveto ( t, x, y );
5671 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5673 if ( t->holding && event->button == 1 )
5676 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5679 if ( event->state & GDK_CONTROL_MASK )
5681 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5683 new_coord = tp->coord;
5687 if ( event->state & GDK_SHIFT_MASK )
5689 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5691 new_coord = wp->coord;
5694 marker_end_move ( t );
5696 // Determine if working on a waypoint or a trackpoint
5697 if ( t->is_waypoint )
5698 vtl->current_wp->coord = new_coord;
5700 if ( vtl->current_tpl ) {
5701 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5704 if ( vtl->current_tp_track )
5705 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5710 vtl->current_wp = NULL;
5711 vtl->current_wp_id = NULL;
5712 trw_layer_cancel_current_tp ( vtl, FALSE );
5714 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5721 Returns true if a waypoint or track is found near the requested event position for this particular layer
5722 The item found is automatically selected
5723 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5725 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5727 if ( event->button != 1 )
5730 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5733 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5736 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5738 if (vtl->waypoints_visible) {
5739 WPSearchParams wp_params;
5740 wp_params.vvp = vvp;
5741 wp_params.x = event->x;
5742 wp_params.y = event->y;
5743 wp_params.closest_wp_id = NULL;
5744 wp_params.closest_wp = NULL;
5746 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5748 if ( wp_params.closest_wp ) {
5751 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5753 // Too easy to move it so must be holding shift to start immediately moving it
5754 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5755 if ( event->state & GDK_SHIFT_MASK ||
5756 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5757 // Put into 'move buffer'
5758 // NB vvp & vw already set in tet
5759 tet->vtl = (gpointer)vtl;
5760 tet->is_waypoint = TRUE;
5762 marker_begin_move (tet, event->x, event->y);
5765 vtl->current_wp = wp_params.closest_wp;
5766 vtl->current_wp_id = wp_params.closest_wp_id;
5768 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5774 if (vtl->tracks_visible) {
5775 TPSearchParams tp_params;
5776 tp_params.vvp = vvp;
5777 tp_params.x = event->x;
5778 tp_params.y = event->y;
5779 tp_params.closest_track_id = NULL;
5780 tp_params.closest_tp = NULL;
5782 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5784 if ( tp_params.closest_tp ) {
5786 // Always select + highlight the track
5787 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5789 tet->is_waypoint = FALSE;
5791 // Select the Trackpoint
5792 // Can move it immediately when control held or it's the previously selected tp
5793 if ( event->state & GDK_CONTROL_MASK ||
5794 vtl->current_tpl == tp_params.closest_tpl ) {
5795 // Put into 'move buffer'
5796 // NB vvp & vw already set in tet
5797 tet->vtl = (gpointer)vtl;
5798 marker_begin_move (tet, event->x, event->y);
5801 vtl->current_tpl = tp_params.closest_tpl;
5802 vtl->current_tp_id = tp_params.closest_track_id;
5803 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5805 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5808 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5810 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5815 /* these aren't the droids you're looking for */
5816 vtl->current_wp = NULL;
5817 vtl->current_wp_id = NULL;
5818 trw_layer_cancel_current_tp ( vtl, FALSE );
5821 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5826 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5828 if ( event->button != 3 )
5831 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5834 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5837 /* Post menu for the currently selected item */
5839 /* See if a track is selected */
5840 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5841 if ( track && track->visible ) {
5843 if ( track->name ) {
5845 if ( vtl->track_right_click_menu )
5846 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5848 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5854 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5856 if ( trkf && udataU.uuid ) {
5858 GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5860 trw_layer_sublayer_add_menu_items ( vtl,
5861 vtl->track_right_click_menu,
5863 VIK_TRW_LAYER_SUBLAYER_TRACK,
5869 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5875 /* See if a waypoint is selected */
5876 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5877 if ( waypoint && waypoint->visible ) {
5878 if ( waypoint->name ) {
5880 if ( vtl->wp_right_click_menu )
5881 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
5883 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5886 udata.wp = waypoint;
5889 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
5891 if ( wpf && udata.uuid ) {
5892 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
5894 trw_layer_sublayer_add_menu_items ( vtl,
5895 vtl->wp_right_click_menu,
5897 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5902 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5911 /* background drawing hook, to be passed the viewport */
5912 static gboolean tool_sync_done = TRUE;
5914 static gboolean tool_sync(gpointer data)
5916 VikViewport *vvp = data;
5917 gdk_threads_enter();
5918 vik_viewport_sync(vvp);
5919 tool_sync_done = TRUE;
5920 gdk_threads_leave();
5924 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
5927 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
5928 gdk_gc_set_function ( t->gc, GDK_INVERT );
5929 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5930 vik_viewport_sync(t->vvp);
5935 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
5937 VikViewport *vvp = t->vvp;
5938 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5939 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5943 if (tool_sync_done) {
5944 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
5945 tool_sync_done = FALSE;
5949 static void marker_end_move ( tool_ed_t *t )
5951 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5952 g_object_unref ( t->gc );
5956 /*** Edit waypoint ****/
5958 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5960 tool_ed_t *t = g_new(tool_ed_t, 1);
5966 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5968 WPSearchParams params;
5969 tool_ed_t *t = data;
5970 VikViewport *vvp = t->vvp;
5972 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5979 if ( !vtl->vl.visible || !vtl->waypoints_visible )
5982 if ( vtl->current_wp && vtl->current_wp->visible )
5984 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5986 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5988 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5989 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5991 if ( event->button == 3 )
5992 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5994 marker_begin_move(t, event->x, event->y);
6001 params.x = event->x;
6002 params.y = event->y;
6003 params.closest_wp_id = NULL;
6004 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6005 params.closest_wp = NULL;
6006 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6007 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
6009 // how do we get here?
6010 marker_begin_move(t, event->x, event->y);
6011 g_critical("shouldn't be here");
6014 else if ( params.closest_wp )
6016 if ( event->button == 3 )
6017 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6019 vtl->waypoint_rightclick = FALSE;
6021 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
6023 vtl->current_wp = params.closest_wp;
6024 vtl->current_wp_id = params.closest_wp_id;
6026 /* could make it so don't update if old WP is off screen and new is null but oh well */
6027 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6031 vtl->current_wp = NULL;
6032 vtl->current_wp_id = NULL;
6033 vtl->waypoint_rightclick = FALSE;
6034 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6038 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6040 tool_ed_t *t = data;
6041 VikViewport *vvp = t->vvp;
6043 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6048 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6051 if ( event->state & GDK_CONTROL_MASK )
6053 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6055 new_coord = tp->coord;
6059 if ( event->state & GDK_SHIFT_MASK )
6061 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6062 if ( wp && wp != vtl->current_wp )
6063 new_coord = wp->coord;
6068 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6070 marker_moveto ( t, x, y );
6077 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6079 tool_ed_t *t = data;
6080 VikViewport *vvp = t->vvp;
6082 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6085 if ( t->holding && event->button == 1 )
6088 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6091 if ( event->state & GDK_CONTROL_MASK )
6093 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6095 new_coord = tp->coord;
6099 if ( event->state & GDK_SHIFT_MASK )
6101 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6102 if ( wp && wp != vtl->current_wp )
6103 new_coord = wp->coord;
6106 marker_end_move ( t );
6108 vtl->current_wp->coord = new_coord;
6109 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6112 /* PUT IN RIGHT PLACE!!! */
6113 if ( event->button == 3 && vtl->waypoint_rightclick )
6115 if ( vtl->wp_right_click_menu )
6116 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
6117 if ( vtl->current_wp ) {
6118 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
6119 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 );
6120 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6122 vtl->waypoint_rightclick = FALSE;
6127 /**** Begin track ***/
6128 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
6133 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6135 vtl->current_track = NULL;
6136 return tool_new_track_click ( vtl, event, vvp );
6139 /*** New track ****/
6141 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
6148 GdkDrawable *drawable;
6154 * Draw specified pixmap
6156 static gboolean draw_sync ( gpointer data )
6158 draw_sync_t *ds = (draw_sync_t*) data;
6159 // Sometimes don't want to draw
6160 // normally because another update has taken precedent such as panning the display
6161 // which means this pixmap is no longer valid
6162 if ( ds->vtl->draw_sync_do ) {
6163 gdk_threads_enter();
6164 gdk_draw_drawable (ds->drawable,
6167 0, 0, 0, 0, -1, -1);
6168 ds->vtl->draw_sync_done = TRUE;
6169 gdk_threads_leave();
6174 static const gchar* distance_string (gdouble distance)
6178 /* draw label with distance */
6179 vik_units_distance_t dist_units = a_vik_get_units_distance ();
6180 switch (dist_units) {
6181 case VIK_UNITS_DISTANCE_MILES:
6182 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
6183 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
6184 } else if (distance < 1609.4) {
6185 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
6187 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
6191 // VIK_UNITS_DISTANCE_KILOMETRES
6192 if (distance >= 1000 && distance < 100000) {
6193 g_sprintf(str, "%3.2f km", distance/1000.0);
6194 } else if (distance < 1000) {
6195 g_sprintf(str, "%d m", (int)distance);
6197 g_sprintf(str, "%d km", (int)distance/1000);
6201 return g_strdup (str);
6205 * Actually set the message in statusbar
6207 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
6209 // Only show elevation data when track has some elevation properties
6210 gchar str_gain_loss[64];
6211 str_gain_loss[0] = '\0';
6213 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
6214 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
6215 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
6217 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
6220 // Write with full gain/loss information
6221 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
6222 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
6227 * Figure out what information should be set in the statusbar and then write it
6229 static void update_statusbar ( VikTrwLayer *vtl )
6231 // Get elevation data
6232 gdouble elev_gain, elev_loss;
6233 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6235 /* Find out actual distance of current track */
6236 gdouble distance = vik_track_get_length (vtl->current_track);
6237 const gchar *str = distance_string (distance);
6239 statusbar_write (str, elev_gain, elev_loss, vtl);
6241 g_free ((gpointer)str);
6245 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
6247 /* if we haven't sync'ed yet, we don't have time to do more. */
6248 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
6249 GList *iter = g_list_last ( vtl->current_track->trackpoints );
6251 static GdkPixmap *pixmap = NULL;
6253 // Need to check in case window has been resized
6254 w1 = vik_viewport_get_width(vvp);
6255 h1 = vik_viewport_get_height(vvp);
6257 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6259 gdk_drawable_get_size (pixmap, &w2, &h2);
6260 if (w1 != w2 || h1 != h2) {
6261 g_object_unref ( G_OBJECT ( pixmap ) );
6262 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6265 // Reset to background
6266 gdk_draw_drawable (pixmap,
6267 vtl->current_track_newpoint_gc,
6268 vik_viewport_get_pixmap(vvp),
6269 0, 0, 0, 0, -1, -1);
6271 draw_sync_t *passalong;
6274 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
6276 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
6277 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
6278 // thus when we come to reset to the background it would include what we have already drawn!!
6279 gdk_draw_line ( pixmap,
6280 vtl->current_track_newpoint_gc,
6281 x1, y1, event->x, event->y );
6282 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
6284 /* Find out actual distance of current track */
6285 gdouble distance = vik_track_get_length (vtl->current_track);
6287 // Now add distance to where the pointer is //
6290 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
6291 vik_coord_to_latlon ( &coord, &ll );
6292 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
6294 // Get elevation data
6295 gdouble elev_gain, elev_loss;
6296 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6298 // Adjust elevation data (if available) for the current pointer position
6300 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
6301 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
6302 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
6303 // Adjust elevation of last track point
6304 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
6306 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
6309 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
6313 const gchar *str = distance_string (distance);
6315 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
6316 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
6318 pango_layout_set_text (pl, str, -1);
6320 pango_layout_get_pixel_size ( pl, &wd, &hd );
6323 // offset from cursor a bit depending on font size
6327 // Create a background block to make the text easier to read over the background map
6328 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
6329 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
6330 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
6332 g_object_unref ( G_OBJECT ( pl ) );
6333 g_object_unref ( G_OBJECT ( background_block_gc ) );
6335 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
6336 passalong->vtl = vtl;
6337 passalong->pixmap = pixmap;
6338 passalong->drawable = GTK_WIDGET(vvp)->window;
6339 passalong->gc = vtl->current_track_newpoint_gc;
6341 // Update statusbar with full gain/loss information
6342 statusbar_write (str, elev_gain, elev_loss, vtl);
6344 g_free ((gpointer)str);
6346 // draw pixmap when we have time to
6347 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
6348 vtl->draw_sync_done = FALSE;
6349 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
6351 return VIK_LAYER_TOOL_ACK;
6354 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
6356 if ( vtl->current_track && event->keyval == GDK_Escape ) {
6357 vtl->current_track = NULL;
6358 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6360 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
6362 if ( vtl->current_track->trackpoints )
6364 GList *last = g_list_last(vtl->current_track->trackpoints);
6365 g_free ( last->data );
6366 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6369 update_statusbar ( vtl );
6371 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6377 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6381 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6384 if ( event->button == 2 ) {
6385 // As the display is panning, the new track pixmap is now invalid so don't draw it
6386 // otherwise this drawing done results in flickering back to an old image
6387 vtl->draw_sync_do = FALSE;
6391 if ( event->button == 3 && vtl->current_track )
6394 if ( vtl->current_track->trackpoints )
6396 GList *last = g_list_last(vtl->current_track->trackpoints);
6397 g_free ( last->data );
6398 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6400 update_statusbar ( vtl );
6402 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6406 if ( event->type == GDK_2BUTTON_PRESS )
6408 /* subtract last (duplicate from double click) tp then end */
6409 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
6411 GList *last = g_list_last(vtl->current_track->trackpoints);
6412 g_free ( last->data );
6413 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6414 /* undo last, then end */
6415 vtl->current_track = NULL;
6417 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6421 if ( ! vtl->current_track )
6423 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
6424 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
6426 vtl->current_track = vik_track_new();
6427 vtl->current_track->visible = TRUE;
6428 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
6430 /* incase it was created by begin track */
6431 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
6436 tp = vik_trackpoint_new();
6437 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
6439 /* snap to other TP */
6440 if ( event->state & GDK_CONTROL_MASK )
6442 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6444 tp->coord = other_tp->coord;
6447 tp->newsegment = FALSE;
6448 tp->has_timestamp = FALSE;
6450 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
6451 /* Auto attempt to get elevation from DEM data (if it's available) */
6452 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
6454 vtl->ct_x1 = vtl->ct_x2;
6455 vtl->ct_y1 = vtl->ct_y2;
6456 vtl->ct_x2 = event->x;
6457 vtl->ct_y2 = event->y;
6459 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6463 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6465 if ( event->button == 2 ) {
6466 // Pan moving ended - enable potential point drawing again
6467 vtl->draw_sync_do = TRUE;
6468 vtl->draw_sync_done = TRUE;
6472 /*** New waypoint ****/
6474 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6479 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6482 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6484 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6485 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
6486 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6491 /*** Edit trackpoint ****/
6493 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6495 tool_ed_t *t = g_new(tool_ed_t, 1);
6501 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6503 tool_ed_t *t = data;
6504 VikViewport *vvp = t->vvp;
6505 TPSearchParams params;
6506 /* OUTDATED DOCUMENTATION:
6507 find 5 pixel range on each side. then put these UTM, and a pointer
6508 to the winning track name (and maybe the winning track itself), and a
6509 pointer to the winning trackpoint, inside an array or struct. pass
6510 this along, do a foreach on the tracks which will do a foreach on the
6513 params.x = event->x;
6514 params.y = event->y;
6515 params.closest_track_id = NULL;
6516 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6517 params.closest_tp = NULL;
6519 if ( event->button != 1 )
6522 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6525 if ( !vtl->vl.visible || !vtl->tracks_visible )
6528 if ( vtl->current_tpl )
6530 /* 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.) */
6531 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6532 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6537 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6539 if ( current_tr->visible &&
6540 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
6541 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6542 marker_begin_move ( t, event->x, event->y );
6548 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6550 if ( params.closest_tp )
6552 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
6553 vtl->current_tpl = params.closest_tpl;
6554 vtl->current_tp_id = params.closest_track_id;
6555 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
6556 trw_layer_tpwin_init ( vtl );
6557 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
6558 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6562 /* these aren't the droids you're looking for */
6566 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6568 tool_ed_t *t = data;
6569 VikViewport *vvp = t->vvp;
6571 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6577 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6580 if ( event->state & GDK_CONTROL_MASK )
6582 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6583 if ( tp && tp != vtl->current_tpl->data )
6584 new_coord = tp->coord;
6586 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6589 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6590 marker_moveto ( t, x, y );
6598 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6600 tool_ed_t *t = data;
6601 VikViewport *vvp = t->vvp;
6603 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6605 if ( event->button != 1)
6610 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6613 if ( event->state & GDK_CONTROL_MASK )
6615 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6616 if ( tp && tp != vtl->current_tpl->data )
6617 new_coord = tp->coord;
6620 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6622 marker_end_move ( t );
6624 /* diff dist is diff from orig */
6626 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6628 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6635 #ifdef VIK_CONFIG_GOOGLE_DIRECTIONS
6636 /*** Route Finder ***/
6637 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6642 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6645 if ( !vtl ) return FALSE;
6646 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6647 if ( event->button == 3 && vtl->route_finder_current_track ) {
6649 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6651 vtl->route_finder_coord = *new_end;
6653 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6654 /* remove last ' to:...' */
6655 if ( vtl->route_finder_current_track->comment ) {
6656 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6657 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6658 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6659 last_to - vtl->route_finder_current_track->comment - 1);
6660 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6665 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6666 struct LatLon start, end;
6667 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6668 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6671 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6672 vik_coord_to_latlon ( &(tmp), &end );
6673 vtl->route_finder_coord = tmp; /* for continuations */
6675 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6676 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6677 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6679 vtl->route_finder_check_added_track = TRUE;
6680 vtl->route_finder_started = FALSE;
6683 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6684 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6685 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6686 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6687 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6688 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
6691 /* see if anything was done -- a track was added or appended to */
6692 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6693 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 ) );
6694 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6695 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6696 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6697 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6699 vtl->route_finder_added_track = NULL;
6700 vtl->route_finder_check_added_track = FALSE;
6701 vtl->route_finder_append = FALSE;
6703 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6705 vtl->route_finder_started = TRUE;
6706 vtl->route_finder_coord = tmp;
6707 vtl->route_finder_current_track = NULL;
6713 /*** Show picture ****/
6715 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6720 /* Params are: vvp, event, last match found or NULL */
6721 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6723 if ( wp->image && wp->visible )
6725 gint x, y, slackx, slacky;
6726 GdkEventButton *event = (GdkEventButton *) params[1];
6728 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6729 slackx = wp->image_width / 2;
6730 slacky = wp->image_height / 2;
6731 if ( x <= event->x + slackx && x >= event->x - slackx
6732 && y <= event->y + slacky && y >= event->y - slacky )
6734 params[2] = wp->image; /* we've found a match. however continue searching
6735 * since we want to find the last match -- that
6736 * is, the match that was drawn last. */
6741 static void trw_layer_show_picture ( gpointer pass_along[6] )
6743 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6745 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
6748 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6749 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6750 g_free ( quoted_file );
6751 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6753 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() );
6754 g_error_free ( err );
6757 #endif /* WINDOWS */
6760 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6762 gpointer params[3] = { vvp, event, NULL };
6763 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6765 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6768 static gpointer pass_along[6];
6769 pass_along[0] = vtl;
6770 pass_along[5] = params[2];
6771 trw_layer_show_picture ( pass_along );
6772 return TRUE; /* found a match */
6775 return FALSE; /* go through other layers, searching for a match */
6778 /***************************************************************************
6780 ***************************************************************************/
6786 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6788 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6789 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6792 /* Structure for thumbnail creating data used in the background thread */
6794 VikTrwLayer *vtl; // Layer needed for redrawing
6795 GSList *pics; // Image list
6796 } thumbnail_create_thread_data;
6798 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6800 guint total = g_slist_length(tctd->pics), done = 0;
6801 while ( tctd->pics )
6803 a_thumbnails_create ( (gchar *) tctd->pics->data );
6804 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6806 return -1; /* Abort thread */
6808 tctd->pics = tctd->pics->next;
6811 // Redraw to show the thumbnails as they are now created
6812 if ( IS_VIK_LAYER(tctd->vtl) )
6813 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6818 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6820 while ( tctd->pics )
6822 g_free ( tctd->pics->data );
6823 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6828 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6830 if ( ! vtl->has_verified_thumbnails )
6832 GSList *pics = NULL;
6833 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6836 gint len = g_slist_length ( pics );
6837 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6838 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6841 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6843 (vik_thr_func) create_thumbnails_thread,
6845 (vik_thr_free_func) thumbnail_create_thread_free,
6853 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6855 return vtl->coord_mode;
6859 * Uniquify the whole layer
6860 * Also requires the layers panel as the names shown there need updating too
6861 * Returns whether the operation was successful or not
6863 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6866 vik_trw_layer_uniquify_tracks ( vtl, vlp );
6867 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
6873 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
6875 vik_coord_convert ( &(wp->coord), *dest_mode );
6878 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6880 vik_track_convert ( tr, *dest_mode );
6883 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6885 if ( vtl->coord_mode != dest_mode )
6887 vtl->coord_mode = dest_mode;
6888 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6889 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6893 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
6895 vtl->menu_selection = selection;
6898 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
6900 return (vtl->menu_selection);
6903 /* ----------- Downloading maps along tracks --------------- */
6905 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6907 /* TODO: calculating based on current size of viewport */
6908 const gdouble w_at_zoom_0_125 = 0.0013;
6909 const gdouble h_at_zoom_0_125 = 0.0011;
6910 gdouble zoom_factor = zoom_level/0.125;
6912 wh->lat = h_at_zoom_0_125 * zoom_factor;
6913 wh->lon = w_at_zoom_0_125 * zoom_factor;
6915 return 0; /* all OK */
6918 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6920 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6921 (dist->lat >= ABS(to->north_south - from->north_south)))
6924 VikCoord *coord = g_malloc(sizeof(VikCoord));
6925 coord->mode = VIK_COORD_LATLON;
6927 if (ABS(gradient) < 1) {
6928 if (from->east_west > to->east_west)
6929 coord->east_west = from->east_west - dist->lon;
6931 coord->east_west = from->east_west + dist->lon;
6932 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
6934 if (from->north_south > to->north_south)
6935 coord->north_south = from->north_south - dist->lat;
6937 coord->north_south = from->north_south + dist->lat;
6938 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
6944 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
6946 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
6947 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
6949 VikCoord *next = from;
6951 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
6953 list = g_list_prepend(list, next);
6959 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
6961 typedef struct _Rect {
6966 #define GLRECT(iter) ((Rect *)((iter)->data))
6969 GList *rects_to_download = NULL;
6972 if (get_download_area_width(vvp, zoom_level, &wh))
6975 GList *iter = tr->trackpoints;
6979 gboolean new_map = TRUE;
6980 VikCoord *cur_coord, tl, br;
6983 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
6985 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6986 rect = g_malloc(sizeof(Rect));
6989 rect->center = *cur_coord;
6990 rects_to_download = g_list_prepend(rects_to_download, rect);
6995 gboolean found = FALSE;
6996 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6997 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7008 GList *fillins = NULL;
7009 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
7010 /* seems that ATM the function get_next_coord works only for LATLON */
7011 if ( cur_coord->mode == VIK_COORD_LATLON ) {
7012 /* fill-ins for far apart points */
7013 GList *cur_rect, *next_rect;
7014 for (cur_rect = rects_to_download;
7015 (next_rect = cur_rect->next) != NULL;
7016 cur_rect = cur_rect->next) {
7017 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
7018 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
7019 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
7023 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
7026 GList *iter = fillins;
7028 cur_coord = (VikCoord *)(iter->data);
7029 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7030 rect = g_malloc(sizeof(Rect));
7033 rect->center = *cur_coord;
7034 rects_to_download = g_list_prepend(rects_to_download, rect);
7039 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7040 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7044 for (iter = fillins; iter; iter = iter->next)
7046 g_list_free(fillins);
7048 if (rects_to_download) {
7049 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
7050 g_free(rect_iter->data);
7051 g_list_free(rects_to_download);
7055 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7058 gint selected_map, default_map;
7059 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
7060 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
7061 gint selected_zoom, default_zoom;
7065 VikTrwLayer *vtl = pass_along[0];
7066 VikLayersPanel *vlp = pass_along[1];
7067 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7068 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
7070 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7071 int num_maps = g_list_length(vmls);
7074 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7078 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
7079 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
7081 gchar **np = map_names;
7082 VikMapsLayer **lp = map_layers;
7083 for (i = 0; i < num_maps; i++) {
7084 gboolean dup = FALSE;
7085 vml = (VikMapsLayer *)(vmls->data);
7086 for (j = 0; j < i; j++) { /* no duplicate allowed */
7087 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
7094 *np++ = vik_maps_layer_get_map_label(vml);
7100 num_maps = lp - map_layers;
7102 for (default_map = 0; default_map < num_maps; default_map++) {
7103 /* TODO: check for parent layer's visibility */
7104 if (VIK_LAYER(map_layers[default_map])->visible)
7107 default_map = (default_map == num_maps) ? 0 : default_map;
7109 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
7110 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
7111 if (cur_zoom == zoom_vals[default_zoom])
7114 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
7116 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
7119 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7122 for (i = 0; i < num_maps; i++)
7123 g_free(map_names[i]);
7131 /**** lowest waypoint number calculation ***/
7132 static gint highest_wp_number_name_to_number(const gchar *name) {
7133 if ( strlen(name) == 3 ) {
7135 if ( n < 100 && name[0] != '0' )
7137 if ( n < 10 && name[0] != '0' )
7145 static void highest_wp_number_reset(VikTrwLayer *vtl)
7147 vtl->highest_wp_number = -1;
7150 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
7152 /* if is bigger that top, add it */
7153 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
7154 if ( new_wp_num > vtl->highest_wp_number )
7155 vtl->highest_wp_number = new_wp_num;
7158 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
7160 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
7161 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
7162 if ( vtl->highest_wp_number == old_wp_num ) {
7164 vtl->highest_wp_number--;
7166 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7167 /* search down until we find something that *does* exist */
7169 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
7170 vtl->highest_wp_number--;
7171 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7176 /* get lowest unused number */
7177 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
7180 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
7182 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
7183 return g_strdup(buf);