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 #ifdef VIK_CONFIG_GOOGLE
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
80 #define VIK_TRW_LAYER_TRACK_GC 16
81 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
82 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
83 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
84 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
85 #define VIK_TRW_LAYER_TRACK_GC_SLOW 13
86 #define VIK_TRW_LAYER_TRACK_GC_AVER 14
87 #define VIK_TRW_LAYER_TRACK_GC_FAST 15
89 #define DRAWMODE_BY_TRACK 0
90 #define DRAWMODE_BY_SPEED 1
91 #define DRAWMODE_ALL_BLACK 2
92 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
93 // as we are (re)calculating the colour for every point
98 /* this is how it knows when you click if you are clicking close to a trackpoint. */
99 #define TRACKPOINT_SIZE_APPROX 5
100 #define WAYPOINT_SIZE_APPROX 5
102 #define MIN_STOP_LENGTH 15
103 #define MAX_STOP_LENGTH 86400
104 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
105 /* this is multiplied by user-inputted value from 1-100. */
107 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
109 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
111 FS_XX_SMALL = 0, // 'xx-small'
114 FS_MEDIUM, // DEFAULT
121 struct _VikTrwLayer {
124 GHashTable *tracks_iters;
125 GHashTable *waypoints_iters;
126 GHashTable *waypoints;
127 GtkTreeIter waypoints_iter, tracks_iter;
128 gboolean tracks_visible, waypoints_visible;
131 guint8 drawpoints_size;
132 guint8 drawelevation;
133 guint8 elevation_factor;
137 guint8 drawdirections;
138 guint8 drawdirections_size;
139 guint8 line_thickness;
140 guint8 bg_line_thickness;
144 gboolean wp_draw_symbols;
145 font_size_t wp_font_size;
147 gdouble track_draw_speed_factor;
149 GdkGC *current_track_gc;
150 // Separate GC for a track's potential new point as drawn via separate method
151 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
152 GdkGC *current_track_newpoint_gc;
155 GdkGC *waypoint_text_gc;
156 GdkGC *waypoint_bg_gc;
157 VikTrack *current_track;
158 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
159 gboolean draw_sync_done;
160 gboolean draw_sync_do;
162 VikCoordMode coord_mode;
164 /* wp editing tool */
165 VikWaypoint *current_wp;
166 gpointer current_wp_id;
168 gboolean waypoint_rightclick;
170 /* track editing tool */
172 VikTrack *current_tp_track;
173 gpointer current_tp_id;
174 VikTrwLayerTpwin *tpwin;
176 /* track editing tool -- more specifically, moving tps */
179 /* route finder tool */
180 gboolean route_finder_started;
181 VikCoord route_finder_coord;
182 gboolean route_finder_check_added_track;
183 VikTrack *route_finder_added_track;
184 VikTrack *route_finder_current_track;
185 gboolean route_finder_append;
192 guint16 image_cache_size;
194 /* for waypoint text */
195 PangoLayout *wplabellayout;
197 gboolean has_verified_thumbnails;
199 GtkMenu *wp_right_click_menu;
200 GtkMenu *track_right_click_menu;
203 VikStdLayerMenuItem menu_selection;
205 gint highest_wp_number;
208 /* A caached waypoint image. */
211 gchar *image; /* filename */
214 struct DrawingParams {
218 guint16 width, height;
219 gdouble cc; // Cosine factor in track directions
220 gdouble ss; // Sine factor in track directions
221 const VikCoord *center;
223 gboolean one_zone, lat_lon;
224 gdouble ce1, ce2, cn1, cn2;
227 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
229 static void trw_layer_delete_item ( gpointer pass_along[6] );
230 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
231 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
233 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
234 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] );
235 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
237 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
238 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
240 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
241 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
243 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
244 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
245 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
246 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
247 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
248 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
249 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
250 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
251 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
252 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
253 static void trw_layer_append_track ( gpointer pass_along[6] );
254 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
255 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
256 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
257 static void trw_layer_split_segments ( gpointer pass_along[6] );
258 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
259 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
260 static void trw_layer_reverse ( gpointer pass_along[6] );
261 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
262 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
263 static void trw_layer_show_picture ( gpointer pass_along[6] );
264 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
266 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
267 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
268 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
269 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
270 static void trw_layer_new_wp ( gpointer lav[2] );
271 static void trw_layer_new_track ( gpointer lav[2] );
272 static void trw_layer_finish_track ( gpointer lav[2] );
273 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
274 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
275 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
276 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
277 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
278 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
279 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
280 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
281 #ifdef VIK_CONFIG_GEOTAG
282 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
283 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
284 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
285 static void trw_layer_geotagging ( gpointer lav[2] );
287 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
288 #ifdef VIK_CONFIG_GOOGLE
289 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
291 #ifdef VIK_CONFIG_OPENSTREETMAP
292 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
294 #ifdef VIK_CONFIG_GEOCACHES
295 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
297 #ifdef VIK_CONFIG_GEOTAG
298 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
300 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
301 static void trw_layer_gps_upload ( gpointer lav[2] );
304 static void trw_layer_properties_item ( gpointer pass_along[7] );
305 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
306 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
308 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
309 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
310 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
312 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
313 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
314 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
315 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
317 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
318 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
319 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
320 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
321 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
322 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
323 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
324 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
325 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
326 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
327 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
328 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
329 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
330 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
331 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
332 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
333 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
334 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
335 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
336 #ifdef VIK_CONFIG_GOOGLE
337 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
338 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
341 static void cached_pixbuf_free ( CachedPixbuf *cp );
342 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
344 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
345 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
347 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
348 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
350 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
351 static void highest_wp_number_reset(VikTrwLayer *vtl);
352 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
353 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
355 // Note for the following tool GtkRadioActionEntry texts:
356 // the very first text value is an internal name not displayed anywhere
357 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
358 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
359 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
360 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
361 static VikToolInterface trw_layer_tools[] = {
362 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
363 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
364 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
366 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
368 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
369 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
370 (VikToolMouseFunc) tool_new_track_click,
371 (VikToolMouseMoveFunc) tool_new_track_move,
372 (VikToolMouseFunc) tool_new_track_release,
373 (VikToolKeyFunc) tool_new_track_key_press,
374 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
375 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
377 { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "<control><shift>B", N_("Begin Track"), 0 },
378 (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
379 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL,
381 GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
383 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
384 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
385 (VikToolMouseFunc) tool_edit_waypoint_click,
386 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
387 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
389 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
391 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
392 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
393 (VikToolMouseFunc) tool_edit_trackpoint_click,
394 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
395 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
397 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
399 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
400 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
401 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
403 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
405 #ifdef VIK_CONFIG_GOOGLE
406 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
407 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
408 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
410 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
413 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
415 /****** PARAMETERS ******/
417 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
418 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
420 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
421 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
423 #define MIN_POINT_SIZE 2
424 #define MAX_POINT_SIZE 10
426 #define MIN_ARROW_SIZE 3
427 #define MAX_ARROW_SIZE 20
429 static VikLayerParamScale params_scales[] = {
430 /* min max step digits */
431 { 1, 10, 1, 0 }, /* line_thickness */
432 { 0, 100, 1, 0 }, /* track draw speed factor */
433 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
434 /* 5 * step == how much to turn */
435 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
436 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
437 { 5, 500, 5, 0 }, // 5: image cache_size - " "
438 { 0, 8, 1, 0 }, // 6: Background line thickness
439 { 1, 64, 1, 0 }, /* wpsize */
440 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
441 { 1, 100, 1, 0 }, // 9: elevation factor
442 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
443 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
446 static gchar* params_font_sizes[] = {
447 N_("Extra Extra Small"),
453 N_("Extra Extra Large"),
456 VikLayerParam trw_layer_params[] = {
457 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
458 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
460 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
461 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
462 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
463 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
464 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
465 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
466 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
467 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
468 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
470 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
471 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
473 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
474 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
475 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
477 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
478 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
479 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
480 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
481 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
482 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
483 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
484 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
485 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
487 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
488 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
489 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
490 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
493 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
495 // Sublayer visibilities
532 *** 1) Add to trw_layer_params and enumeration
533 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
536 /****** END PARAMETERS ******/
538 static VikTrwLayer* trw_layer_new ( gint drawmode );
539 /* Layer Interface function definitions */
540 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
541 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
542 static void trw_layer_free ( VikTrwLayer *trwlayer );
543 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
544 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
545 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
546 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
547 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
548 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
549 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
550 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
551 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
552 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
553 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
554 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
555 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
556 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
557 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
558 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
559 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
560 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
561 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
562 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
563 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
564 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
565 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
566 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
567 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
568 /* End Layer Interface function definitions */
570 VikLayerInterface vik_trw_layer_interface = {
577 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
581 params_groups, /* params_groups */
582 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
586 (VikLayerFuncCreate) trw_layer_create,
587 (VikLayerFuncRealize) trw_layer_realize,
588 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
589 (VikLayerFuncFree) trw_layer_free,
591 (VikLayerFuncProperties) NULL,
592 (VikLayerFuncDraw) trw_layer_draw,
593 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
595 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
596 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
598 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
599 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
601 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
602 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
603 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
604 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
605 (VikLayerFuncLayerSelected) trw_layer_selected,
607 (VikLayerFuncMarshall) trw_layer_marshall,
608 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
610 (VikLayerFuncSetParam) trw_layer_set_param,
611 (VikLayerFuncGetParam) trw_layer_get_param,
613 (VikLayerFuncReadFileData) a_gpspoint_read_file,
614 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
616 (VikLayerFuncDeleteItem) trw_layer_del_item,
617 (VikLayerFuncCutItem) trw_layer_cut_item,
618 (VikLayerFuncCopyItem) trw_layer_copy_item,
619 (VikLayerFuncPasteItem) trw_layer_paste_item,
620 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
622 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
624 (VikLayerFuncSelectClick) trw_layer_select_click,
625 (VikLayerFuncSelectMove) trw_layer_select_move,
626 (VikLayerFuncSelectRelease) trw_layer_select_release,
627 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
636 GType vik_trw_layer_get_type ()
638 static GType vtl_type = 0;
642 static const GTypeInfo vtl_info =
644 sizeof (VikTrwLayerClass),
645 NULL, /* base_init */
646 NULL, /* base_finalize */
647 NULL, /* class init */
648 NULL, /* class_finalize */
649 NULL, /* class_data */
650 sizeof (VikTrwLayer),
652 NULL /* instance init */
654 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
660 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
662 static gpointer pass_along[6];
668 pass_along[1] = NULL;
669 pass_along[2] = GINT_TO_POINTER (subtype);
670 pass_along[3] = sublayer;
671 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
672 pass_along[5] = NULL;
674 trw_layer_delete_item ( pass_along );
677 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
679 static gpointer pass_along[6];
685 pass_along[1] = NULL;
686 pass_along[2] = GINT_TO_POINTER (subtype);
687 pass_along[3] = sublayer;
688 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
689 pass_along[5] = NULL;
691 trw_layer_copy_item_cb(pass_along);
692 trw_layer_cut_item_cb(pass_along);
695 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
697 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
698 gint subtype = GPOINTER_TO_INT (pass_along[2]);
699 gpointer * sublayer = pass_along[3];
703 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
707 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
708 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
709 if ( wp && wp->name )
712 name = NULL; // Broken :(
715 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
716 if ( trk && trk->name )
719 name = NULL; // Broken :(
722 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
723 subtype, len, name, data);
727 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
729 trw_layer_copy_item_cb(pass_along);
730 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
731 trw_layer_delete_item(pass_along);
734 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
745 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
747 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
748 // 'Simple' memory copy of byte array from the marshalling above
749 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
750 fi = g_malloc ( *len );
752 memcpy(fi->data, id, il);
755 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
756 // less magic than before...
757 *len = sizeof(FlatItem) + 1 + il;
758 fi = g_malloc ( *len );
760 memcpy(fi->data, id, il);
764 *item = (guint8 *)fi;
767 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
769 FlatItem *fi = (FlatItem *) item;
771 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
776 w = vik_waypoint_unmarshall(fi->data, fi->len);
777 // When copying - we'll create a new name based on the original
778 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
779 vik_trw_layer_add_waypoint ( vtl, name, w );
780 waypoint_convert (NULL, w, &vtl->coord_mode);
782 // Consider if redraw necessary for the new item
783 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
784 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
787 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
792 t = vik_track_unmarshall(fi->data, fi->len);
793 // When copying - we'll create a new name based on the original
794 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
795 vik_trw_layer_add_track ( vtl, name, t );
796 track_convert (name, t, &vtl->coord_mode);
798 // Consider if redraw necessary for the new item
799 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
800 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
806 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
813 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
817 case PARAM_TV: vtl->tracks_visible = data.b; break;
818 case PARAM_WV: vtl->waypoints_visible = data.b; break;
819 case PARAM_DM: vtl->drawmode = data.u; break;
820 case PARAM_DP: vtl->drawpoints = data.b; break;
822 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
823 vtl->drawpoints_size = data.u;
825 case PARAM_DE: vtl->drawelevation = data.b; break;
826 case PARAM_DS: vtl->drawstops = data.b; break;
827 case PARAM_DL: vtl->drawlines = data.b; break;
828 case PARAM_DD: vtl->drawdirections = data.b; break;
830 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
831 vtl->drawdirections_size = data.u;
833 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
834 vtl->stop_length = data.u;
836 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
837 vtl->elevation_factor = data.u;
839 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
841 vtl->line_thickness = data.u;
842 trw_layer_new_track_gcs ( vtl, vp );
845 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
847 vtl->bg_line_thickness = data.u;
848 trw_layer_new_track_gcs ( vtl, vp );
851 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
852 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
853 case PARAM_DLA: vtl->drawlabels = data.b; break;
854 case PARAM_DI: vtl->drawimages = data.b; break;
855 case PARAM_IS: if ( data.u != vtl->image_size )
857 vtl->image_size = data.u;
858 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
859 g_queue_free ( vtl->image_cache );
860 vtl->image_cache = g_queue_new ();
863 case PARAM_IA: vtl->image_alpha = data.u; break;
864 case PARAM_ICS: vtl->image_cache_size = data.u;
865 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
866 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
868 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
869 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
870 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
871 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
872 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
873 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
874 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
875 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
880 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
882 VikLayerParamData rv;
885 case PARAM_TV: rv.b = vtl->tracks_visible; break;
886 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
887 case PARAM_DM: rv.u = vtl->drawmode; break;
888 case PARAM_DP: rv.b = vtl->drawpoints; break;
889 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
890 case PARAM_DE: rv.b = vtl->drawelevation; break;
891 case PARAM_EF: rv.u = vtl->elevation_factor; break;
892 case PARAM_DS: rv.b = vtl->drawstops; break;
893 case PARAM_SL: rv.u = vtl->stop_length; break;
894 case PARAM_DL: rv.b = vtl->drawlines; break;
895 case PARAM_DD: rv.b = vtl->drawdirections; break;
896 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
897 case PARAM_LT: rv.u = vtl->line_thickness; break;
898 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
899 case PARAM_DLA: rv.b = vtl->drawlabels; break;
900 case PARAM_DI: rv.b = vtl->drawimages; break;
901 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
902 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
903 case PARAM_IS: rv.u = vtl->image_size; break;
904 case PARAM_IA: rv.u = vtl->image_alpha; break;
905 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
906 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
907 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
908 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
909 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
910 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
911 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
912 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
913 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
918 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
929 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
930 a_gpx_write_file(vtl, f, NULL);
931 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
934 g_file_get_contents(tmpname, &dd, &dl, NULL);
935 *len = sizeof(pl) + pl + dl;
936 *data = g_malloc(*len);
937 memcpy(*data, &pl, sizeof(pl));
938 memcpy(*data + sizeof(pl), pd, pl);
939 memcpy(*data + sizeof(pl) + pl, dd, dl);
948 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
950 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
956 memcpy(&pl, data, sizeof(pl));
958 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
961 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
962 g_critical("couldn't open temp file");
965 fwrite(data, len - pl - sizeof(pl), 1, f);
967 a_gpx_read_file(rv, f);
975 // Keep interesting hash function at least visible
977 static guint strcase_hash(gconstpointer v)
979 // 31 bit hash function
982 gchar s[128]; // malloc is too slow for reading big files
985 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
986 p[i] = toupper(t[i]);
992 for (p += 1; *p != '\0'; p++)
993 h = (h << 5) - h + *p;
1000 static VikTrwLayer* trw_layer_new ( gint drawmode )
1002 if (trw_layer_params[PARAM_DM].widget_data == NULL)
1003 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
1004 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
1005 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
1007 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1008 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1010 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1011 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1013 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1014 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1015 // and with normal PC processing capabilities - it has negligibile performance impact
1016 // This also minimized the amount of rework - as the management of the hash tables already exists.
1018 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1019 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1020 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1022 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1023 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1024 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1025 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1027 /* TODO: constants at top */
1028 rv->waypoints_visible = rv->tracks_visible = TRUE;
1029 rv->drawmode = drawmode;
1030 rv->drawpoints = TRUE;
1031 rv->drawpoints_size = MIN_POINT_SIZE;
1032 rv->drawdirections_size = 5;
1033 rv->drawstops = FALSE;
1034 rv->drawelevation = FALSE;
1035 rv->elevation_factor = 30;
1036 rv->stop_length = 60;
1037 rv->drawlines = TRUE;
1038 rv->wplabellayout = NULL;
1039 rv->wp_right_click_menu = NULL;
1040 rv->track_right_click_menu = NULL;
1041 rv->waypoint_gc = NULL;
1042 rv->waypoint_text_gc = NULL;
1043 rv->waypoint_bg_gc = NULL;
1044 rv->track_gc = NULL;
1045 rv->track_draw_speed_factor = 30.0;
1046 rv->line_thickness = 1;
1047 rv->bg_line_thickness = 0;
1048 rv->current_wp = NULL;
1049 rv->current_wp_id = NULL;
1050 rv->current_track = NULL;
1051 rv->current_tpl = NULL;
1052 rv->current_tp_track = NULL;
1053 rv->current_tp_id = NULL;
1054 rv->moving_tp = FALSE;
1055 rv->moving_wp = FALSE;
1057 rv->draw_sync_done = TRUE;
1058 rv->draw_sync_do = TRUE;
1060 rv->route_finder_started = FALSE;
1061 rv->route_finder_check_added_track = FALSE;
1062 rv->route_finder_current_track = NULL;
1063 rv->route_finder_append = FALSE;
1065 rv->waypoint_rightclick = FALSE;
1067 rv->image_cache = g_queue_new();
1068 rv->image_size = 64;
1069 rv->image_alpha = 255;
1070 rv->image_cache_size = 300;
1071 rv->drawimages = TRUE;
1072 rv->drawlabels = TRUE;
1077 static void trw_layer_free ( VikTrwLayer *trwlayer )
1079 g_hash_table_destroy(trwlayer->waypoints);
1080 g_hash_table_destroy(trwlayer->tracks);
1082 /* ODC: replace with GArray */
1083 trw_layer_free_track_gcs ( trwlayer );
1085 if ( trwlayer->wp_right_click_menu )
1086 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1088 if ( trwlayer->track_right_click_menu )
1089 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1091 if ( trwlayer->wplabellayout != NULL)
1092 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1094 if ( trwlayer->waypoint_gc != NULL )
1095 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1097 if ( trwlayer->waypoint_text_gc != NULL )
1098 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1100 if ( trwlayer->waypoint_bg_gc != NULL )
1101 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1103 if ( trwlayer->tpwin != NULL )
1104 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1106 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1107 g_queue_free ( trwlayer->image_cache );
1110 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1114 dp->xmpp = vik_viewport_get_xmpp ( vp );
1115 dp->ympp = vik_viewport_get_ympp ( vp );
1116 dp->width = vik_viewport_get_width ( vp );
1117 dp->height = vik_viewport_get_height ( vp );
1118 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1119 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1121 dp->center = vik_viewport_get_center ( vp );
1122 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1123 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1128 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1129 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1130 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1132 dp->ce1 = dp->center->east_west-w2;
1133 dp->ce2 = dp->center->east_west+w2;
1134 dp->cn1 = dp->center->north_south-h2;
1135 dp->cn2 = dp->center->north_south+h2;
1136 } else if ( dp->lat_lon ) {
1137 VikCoord upperleft, bottomright;
1138 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1139 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1140 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1141 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1142 dp->ce1 = upperleft.east_west;
1143 dp->ce2 = bottomright.east_west;
1144 dp->cn1 = bottomright.north_south;
1145 dp->cn2 = upperleft.north_south;
1148 dp->track_gc_iter = 0;
1152 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1153 * Here a simple traffic like light colour system is used:
1154 * . slow points are red
1155 * . average is yellow
1156 * . fast points are green
1158 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1161 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1162 if ( average_speed > 0 ) {
1163 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1164 if ( rv < low_speed )
1165 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1166 else if ( rv > high_speed )
1167 return VIK_TRW_LAYER_TRACK_GC_FAST;
1169 return VIK_TRW_LAYER_TRACK_GC_AVER;
1172 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1175 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1177 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1178 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1179 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1180 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1183 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1185 /* TODO: this function is a mess, get rid of any redundancy */
1186 GList *list = track->trackpoints;
1188 gboolean useoldvals = TRUE;
1190 gboolean drawpoints;
1192 gboolean drawelevation;
1193 gdouble min_alt, max_alt, alt_diff = 0;
1195 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1196 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1199 if ( dp->vtl->drawelevation )
1201 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1202 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1203 alt_diff = max_alt - min_alt;
1206 if ( ! track->visible )
1209 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1210 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1211 trw_layer_draw_track ( name, track, dp, TRUE );
1213 if ( draw_track_outline )
1214 drawpoints = drawstops = FALSE;
1216 drawpoints = dp->vtl->drawpoints;
1217 drawstops = dp->vtl->drawstops;
1220 gboolean drawing_highlight = FALSE;
1221 /* Current track - used for creation */
1222 if ( track == dp->vtl->current_track )
1223 main_gc = dp->vtl->current_track_gc;
1225 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1226 /* Draw all tracks of the layer in special colour */
1227 /* if track is member of selected layer or is the current selected track
1228 then draw in the highlight colour.
1229 NB this supercedes the drawmode */
1230 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1231 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1232 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1233 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1234 drawing_highlight = TRUE;
1237 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1238 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1240 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1244 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1245 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1247 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1252 int x, y, oldx, oldy;
1253 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1255 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1257 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1259 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1261 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1262 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1268 gdouble average_speed = 0.0;
1269 gdouble low_speed = 0.0;
1270 gdouble high_speed = 0.0;
1271 // If necessary calculate these values - which is done only once per track redraw
1272 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1273 // the percentage factor away from the average speed determines transistions between the levels
1274 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1275 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1276 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1279 while ((list = g_list_next(list)))
1281 tp = VIK_TRACKPOINT(list->data);
1282 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1284 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1285 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1286 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1287 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1288 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1290 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1293 * If points are the same in display coordinates, don't draw.
1295 if ( useoldvals && x == oldx && y == oldy )
1297 // Still need to process points to ensure 'stops' are drawn if required
1298 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1299 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1300 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 );
1305 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1306 if ( drawpoints || dp->vtl->drawlines ) {
1307 // setup main_gc for both point and line drawing
1308 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1309 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1310 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1314 if ( drawpoints && ! draw_track_outline )
1319 * The concept of drawing stops is that a trackpoint
1320 * that is if the next trackpoint has a timestamp far into
1321 * the future, we draw a circle of 6x trackpoint size,
1322 * instead of a rectangle of 2x trackpoint size.
1323 * This is drawn first so the trackpoint will be drawn on top
1326 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1327 /* Stop point. Draw 6x circle. */
1328 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 );
1330 /* Regular point - draw 2x square. */
1331 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1334 /* Final point - draw 4x circle. */
1335 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 );
1338 if ((!tp->newsegment) && (dp->vtl->drawlines))
1341 /* UTM only: zone check */
1342 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1343 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1346 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1348 if ( draw_track_outline ) {
1349 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);
1355 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1357 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1362 tmp[1].y = oldy-FIXALTITUDE(list->data);
1364 tmp[2].y = y-FIXALTITUDE(list->next->data);
1369 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1370 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1372 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1373 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1375 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1380 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1381 // Draw an arrow at the mid point to show the direction of the track
1382 // Code is a rework from vikwindow::draw_ruler()
1383 gint midx = (oldx + x) / 2;
1384 gint midy = (oldy + y) / 2;
1386 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1387 // Avoid divide by zero and ensure at least 1 pixel big
1389 gdouble dx = (oldx - midx) / len;
1390 gdouble dy = (oldy - midy) / len;
1391 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1392 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1402 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1404 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1405 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1407 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1409 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1410 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1411 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1415 * If points are the same in display coordinates, don't draw.
1417 if ( x != oldx || y != oldy )
1419 if ( draw_track_outline )
1420 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1422 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1428 * If points are the same in display coordinates, don't draw.
1430 if ( x != oldx && y != oldy )
1432 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1433 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1441 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1442 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
1443 dp->track_gc_iter = 0;
1446 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1447 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1449 trw_layer_draw_track ( name, track, dp, FALSE );
1452 static void cached_pixbuf_free ( CachedPixbuf *cp )
1454 g_object_unref ( G_OBJECT(cp->pixbuf) );
1455 g_free ( cp->image );
1458 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1460 return strcmp ( cp->image, name );
1463 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1466 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1467 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1468 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1471 GdkPixbuf *sym = NULL;
1472 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1474 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1476 if ( wp->image && dp->vtl->drawimages )
1478 GdkPixbuf *pixbuf = NULL;
1481 if ( dp->vtl->image_alpha == 0)
1484 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1486 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1489 gchar *image = wp->image;
1490 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1491 if ( ! regularthumb )
1493 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1494 image = "\x12\x00"; /* this shouldn't occur naturally. */
1498 CachedPixbuf *cp = NULL;
1499 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1500 if ( dp->vtl->image_size == 128 )
1501 cp->pixbuf = regularthumb;
1504 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1505 g_assert ( cp->pixbuf );
1506 g_object_unref ( G_OBJECT(regularthumb) );
1508 cp->image = g_strdup ( image );
1510 /* needed so 'click picture' tool knows how big the pic is; we don't
1511 * store it in cp because they may have been freed already. */
1512 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1513 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1515 g_queue_push_head ( dp->vtl->image_cache, cp );
1516 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1517 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1519 pixbuf = cp->pixbuf;
1523 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1529 w = gdk_pixbuf_get_width ( pixbuf );
1530 h = gdk_pixbuf_get_height ( pixbuf );
1532 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1534 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1535 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1536 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1537 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1538 // Highlighted - so draw a little border around the chosen one
1539 // single line seems a little weak so draw 2 of them
1540 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1541 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1542 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1543 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1546 if ( dp->vtl->image_alpha == 255 )
1547 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1549 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1551 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1555 /* DRAW ACTUAL DOT */
1556 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1557 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 );
1559 else if ( wp == dp->vtl->current_wp ) {
1560 switch ( dp->vtl->wp_symbol ) {
1561 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;
1562 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;
1563 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;
1564 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 );
1565 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 );
1569 switch ( dp->vtl->wp_symbol ) {
1570 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;
1571 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;
1572 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;
1573 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 );
1574 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;
1578 if ( dp->vtl->drawlabels )
1580 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1581 gint label_x, label_y;
1583 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1585 // Could this stored in the waypoint rather than recreating each pass?
1586 gchar *fsize = NULL;
1587 switch (dp->vtl->wp_font_size) {
1588 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1589 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1590 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1591 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1592 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1593 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1594 default: fsize = g_strdup ( "medium" ); break;
1597 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1599 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1600 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1602 // Fallback if parse failure
1603 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1605 g_free ( wp_label_markup );
1608 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1609 label_x = x - width/2;
1611 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1613 label_y = y - dp->vtl->wp_size - height - 2;
1615 /* if highlight mode on, then draw background text in highlight colour */
1616 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1617 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1618 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1619 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1620 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1622 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1625 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1627 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1632 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1634 static struct DrawingParams dp;
1635 g_assert ( l != NULL );
1637 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1639 if ( l->tracks_visible )
1640 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1642 if (l->waypoints_visible)
1643 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1646 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1649 if ( vtl->track_bg_gc )
1651 g_object_unref ( vtl->track_bg_gc );
1652 vtl->track_bg_gc = NULL;
1654 if ( vtl->current_track_gc )
1656 g_object_unref ( vtl->current_track_gc );
1657 vtl->current_track_gc = NULL;
1659 if ( vtl->current_track_newpoint_gc )
1661 g_object_unref ( vtl->current_track_newpoint_gc );
1662 vtl->current_track_newpoint_gc = NULL;
1665 if ( ! vtl->track_gc )
1667 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1668 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1669 g_array_free ( vtl->track_gc, TRUE );
1670 vtl->track_gc = NULL;
1673 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1675 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1676 gint width = vtl->line_thickness;
1678 if ( vtl->track_gc )
1679 trw_layer_free_track_gcs ( vtl );
1681 if ( vtl->track_bg_gc )
1682 g_object_unref ( vtl->track_bg_gc );
1683 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1685 // Ensure new track drawing heeds line thickness setting
1686 // however always have a minium of 2, as 1 pixel is really narrow
1687 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1689 if ( vtl->current_track_gc )
1690 g_object_unref ( vtl->current_track_gc );
1691 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1692 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1694 // 'newpoint' gc is exactly the same as the current track gc
1695 if ( vtl->current_track_newpoint_gc )
1696 g_object_unref ( vtl->current_track_newpoint_gc );
1697 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1698 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1700 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1702 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1704 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1705 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1706 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1707 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1708 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1709 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1710 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1711 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1712 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1713 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1715 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1717 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1719 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1720 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1721 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1723 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1726 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1728 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1729 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1731 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1732 /* early exit, as the rest is GUI related */
1736 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1737 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1739 trw_layer_new_track_gcs ( rv, vp );
1741 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1742 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1743 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1744 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1746 rv->has_verified_thumbnails = FALSE;
1747 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1749 rv->wp_draw_symbols = TRUE;
1750 rv->wp_font_size = FS_MEDIUM;
1752 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1754 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1759 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1761 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1763 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1764 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 );
1766 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 );
1769 *new_iter = *((GtkTreeIter *) pass_along[1]);
1770 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1772 if ( ! track->visible )
1773 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1776 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1778 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1779 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1780 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 );
1782 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 );
1785 *new_iter = *((GtkTreeIter *) pass_along[1]);
1786 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1788 if ( ! wp->visible )
1789 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1792 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1794 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1795 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1797 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1801 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1803 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1804 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1806 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1810 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1813 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1815 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1816 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1817 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1819 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1822 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
1823 trw_layer_add_sublayer_waypoints ( vtl, vt , layer_iter );
1825 pass_along[0] = &(vtl->waypoints_iter);
1826 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1828 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1830 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1834 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1838 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1839 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1840 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1842 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1844 return (t->visible ^= 1);
1848 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1850 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1852 return (t->visible ^= 1);
1861 * Return a property about tracks for this layer
1863 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1865 return vtl->line_thickness;
1868 // Structure to hold multiple track information for a layer
1877 * Build up layer multiple track information via updating the tooltip_tracks structure
1879 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1881 tt->length = tt->length + vik_track_get_length (tr);
1883 // Ensure times are available
1884 if ( tr->trackpoints &&
1885 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1886 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1889 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1890 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1892 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1893 // Hence initialize to the first 'proper' value
1894 if ( tt->start_time == 0 )
1895 tt->start_time = t1;
1896 if ( tt->end_time == 0 )
1899 // Update find the earliest / last times
1900 if ( t1 < tt->start_time )
1901 tt->start_time = t1;
1902 if ( t2 > tt->end_time )
1905 // Keep track of total time
1906 // there maybe gaps within a track (eg segments)
1907 // but this should be generally good enough for a simple indicator
1908 tt->duration = tt->duration + (int)(t2-t1);
1913 * Generate tooltip text for the layer.
1914 * This is relatively complicated as it considers information for
1915 * no tracks, a single track or multiple tracks
1916 * (which may or may not have timing information)
1918 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1929 static gchar tmp_buf[128];
1932 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1934 // Safety check - I think these should always be valid
1935 if ( vtl->tracks && vtl->waypoints ) {
1936 tooltip_tracks tt = { 0.0, 0, 0 };
1937 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1939 GDate* gdate_start = g_date_new ();
1940 g_date_set_time_t (gdate_start, tt.start_time);
1942 GDate* gdate_end = g_date_new ();
1943 g_date_set_time_t (gdate_end, tt.end_time);
1945 if ( g_date_compare (gdate_start, gdate_end) ) {
1946 // Dates differ so print range on separate line
1947 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1948 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1949 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1952 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1953 if ( tt.start_time != 0 )
1954 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1958 if ( tt.length > 0.0 ) {
1959 gdouble len_in_units;
1961 // Setup info dependent on distance units
1962 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1963 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1964 len_in_units = VIK_METERS_TO_MILES(tt.length);
1967 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1968 len_in_units = tt.length/1000.0;
1971 // Timing information if available
1973 if ( tt.duration > 0 ) {
1974 g_snprintf (tbuf1, sizeof(tbuf1),
1975 _(" in %d:%02d hrs:mins"),
1976 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1978 g_snprintf (tbuf2, sizeof(tbuf2),
1979 _("\n%sTotal Length %.1f %s%s"),
1980 tbuf3, len_in_units, tbuf4, tbuf1);
1983 // Put together all the elements to form compact tooltip text
1984 g_snprintf (tmp_buf, sizeof(tmp_buf),
1985 _("Tracks: %d - Waypoints: %d%s"),
1986 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1988 g_date_free (gdate_start);
1989 g_date_free (gdate_end);
1996 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2000 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2002 // Very simple tooltip - may expand detail in the future...
2003 static gchar tmp_buf[32];
2004 g_snprintf (tmp_buf, sizeof(tmp_buf),
2006 g_hash_table_size (l->tracks));
2010 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2012 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
2014 // Could be a better way of handling strings - but this works...
2015 gchar time_buf1[20];
2016 gchar time_buf2[20];
2017 time_buf1[0] = '\0';
2018 time_buf2[0] = '\0';
2019 static gchar tmp_buf[100];
2020 // Compact info: Short date eg (11/20/99), duration and length
2021 // Hopefully these are the things that are most useful and so promoted into the tooltip
2022 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2023 // %x The preferred date representation for the current locale without the time.
2024 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2025 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2026 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2028 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2031 // Get length and consider the appropriate distance units
2032 gdouble tr_len = vik_track_get_length(tr);
2033 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2034 switch (dist_units) {
2035 case VIK_UNITS_DISTANCE_KILOMETRES:
2036 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2038 case VIK_UNITS_DISTANCE_MILES:
2039 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2048 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2050 // Very simple tooltip - may expand detail in the future...
2051 static gchar tmp_buf[32];
2052 g_snprintf (tmp_buf, sizeof(tmp_buf),
2054 g_hash_table_size (l->waypoints));
2058 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2060 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2061 // NB It's OK to return NULL
2072 * Function to show basic track point information on the statusbar
2074 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2077 switch (a_vik_get_units_height ()) {
2078 case VIK_UNITS_HEIGHT_FEET:
2079 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2082 //VIK_UNITS_HEIGHT_METRES:
2083 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2088 if ( trkpt->has_timestamp ) {
2089 // Compact date time format
2090 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2094 // Position is put later on, as this bit may not be seen if the display is not big enough,
2095 // one can easily use the current pointer position to see this if needed
2096 gchar *lat = NULL, *lon = NULL;
2097 static struct LatLon ll;
2098 vik_coord_to_latlon (&(trkpt->coord), &ll);
2099 a_coords_latlon_to_string ( &ll, &lat, &lon );
2102 // Again is put later on, as this bit may not be seen if the display is not big enough
2103 // trackname can be seen from the treeview (when enabled)
2104 // Also name could be very long to not leave room for anything else
2107 if ( vtl->current_tp_track ) {
2108 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2111 // Combine parts to make overall message
2112 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2113 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2120 * Function to show basic waypoint information on the statusbar
2122 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2125 switch (a_vik_get_units_height ()) {
2126 case VIK_UNITS_HEIGHT_FEET:
2127 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2130 //VIK_UNITS_HEIGHT_METRES:
2131 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2135 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2136 // one can easily use the current pointer position to see this if needed
2137 gchar *lat = NULL, *lon = NULL;
2138 static struct LatLon ll;
2139 vik_coord_to_latlon (&(wpt->coord), &ll);
2140 a_coords_latlon_to_string ( &ll, &lat, &lon );
2142 // Combine parts to make overall message
2145 // Add comment if available
2146 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2148 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2149 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2156 * General layer selection function, find out which bit is selected and take appropriate action
2158 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2161 l->current_wp = NULL;
2162 l->current_wp_id = NULL;
2163 trw_layer_cancel_current_tp ( l, FALSE );
2166 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2170 case VIK_TREEVIEW_TYPE_LAYER:
2172 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2173 /* Mark for redraw */
2178 case VIK_TREEVIEW_TYPE_SUBLAYER:
2182 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2184 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2185 /* Mark for redraw */
2189 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2191 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2192 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2193 /* Mark for redraw */
2197 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2199 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2200 /* Mark for redraw */
2204 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2206 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2208 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2209 // Show some waypoint info
2210 set_statusbar_msg_info_wpt ( l, wpt );
2211 /* Mark for redraw */
2218 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2227 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2232 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2237 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2239 return l->waypoints;
2243 * ATM use a case sensitive find
2244 * Finds the first one
2246 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2248 if ( wp && wp->name )
2249 if ( ! strcmp ( wp->name, name ) )
2255 * Get waypoint by name - not guaranteed to be unique
2256 * Finds the first one
2258 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2260 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2264 * ATM use a case sensitive find
2265 * Finds the first one
2267 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2269 if ( trk && trk->name )
2270 if ( ! strcmp ( trk->name, name ) )
2276 * Get track by name - not guaranteed to be unique
2277 * Finds the first one
2279 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2281 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2284 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2286 static VikCoord fixme;
2287 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2288 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2289 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2290 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2291 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2292 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2293 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2294 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2295 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2298 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2300 GList *tr = trk->trackpoints;
2301 static VikCoord fixme;
2305 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2306 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2307 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2308 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2309 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2310 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2311 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2312 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2313 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2318 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2320 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2321 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2323 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2324 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2325 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2326 maxmin[0].lat = wpt_maxmin[0].lat;
2329 maxmin[0].lat = trk_maxmin[0].lat;
2331 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2332 maxmin[0].lon = wpt_maxmin[0].lon;
2335 maxmin[0].lon = trk_maxmin[0].lon;
2337 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2338 maxmin[1].lat = wpt_maxmin[1].lat;
2341 maxmin[1].lat = trk_maxmin[1].lat;
2343 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2344 maxmin[1].lon = wpt_maxmin[1].lon;
2347 maxmin[1].lon = trk_maxmin[1].lon;
2351 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2353 /* 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... */
2354 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2355 trw_layer_find_maxmin (vtl, maxmin);
2356 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2360 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2361 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2366 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2369 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2370 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2372 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2375 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2377 /* First set the center [in case previously viewing from elsewhere] */
2378 /* Then loop through zoom levels until provided positions are in view */
2379 /* This method is not particularly fast - but should work well enough */
2380 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2382 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2383 vik_viewport_set_center_coord ( vvp, &coord );
2385 /* Convert into definite 'smallest' and 'largest' positions */
2386 struct LatLon minmin;
2387 if ( maxmin[0].lat < maxmin[1].lat )
2388 minmin.lat = maxmin[0].lat;
2390 minmin.lat = maxmin[1].lat;
2392 struct LatLon maxmax;
2393 if ( maxmin[0].lon > maxmin[1].lon )
2394 maxmax.lon = maxmin[0].lon;
2396 maxmax.lon = maxmin[1].lon;
2398 /* Never zoom in too far - generally not that useful, as too close ! */
2399 /* Always recalculate the 'best' zoom level */
2401 vik_viewport_set_zoom ( vvp, zoom );
2403 gdouble min_lat, max_lat, min_lon, max_lon;
2404 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2405 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2406 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2407 /* NB I think the logic used in this test to determine if the bounds is within view
2408 fails if track goes across 180 degrees longitude.
2409 Hopefully that situation is not too common...
2410 Mind you viking doesn't really do edge locations to well anyway */
2411 if ( min_lat < minmin.lat &&
2412 max_lat > minmin.lat &&
2413 min_lon < maxmax.lon &&
2414 max_lon > maxmax.lon )
2415 /* Found within zoom level */
2420 vik_viewport_set_zoom ( vvp, zoom );
2424 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2426 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2427 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2428 trw_layer_find_maxmin (vtl, maxmin);
2429 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2432 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2437 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2439 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])) ) ) {
2440 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2443 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2446 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2448 GtkWidget *file_selector;
2450 gboolean failed = FALSE;
2451 file_selector = gtk_file_chooser_dialog_new (title,
2453 GTK_FILE_CHOOSER_ACTION_SAVE,
2454 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2455 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2457 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2459 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2461 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2462 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2464 gtk_widget_hide ( file_selector );
2465 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2470 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2472 gtk_widget_hide ( file_selector );
2473 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2478 gtk_widget_destroy ( file_selector );
2480 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2483 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2485 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2488 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2490 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2493 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2495 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2496 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2497 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2498 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2500 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2502 g_free ( auto_save_name );
2505 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2507 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2508 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2509 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2510 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2512 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2514 g_free ( auto_save_name );
2518 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2521 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2523 gchar *name_used = NULL;
2526 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2527 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2529 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2533 gchar *quoted_file = g_shell_quote ( name_used );
2534 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2535 g_free ( quoted_file );
2536 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2538 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2539 g_error_free ( err );
2543 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2544 //g_remove ( name_used );
2545 // Perhaps should be deleted when the program ends?
2546 // For now leave it to the user to delete it / use system temp cleanup methods.
2547 g_free ( name_used );
2551 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2553 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2556 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2558 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2561 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2563 gpointer layer_and_vlp[2];
2564 layer_and_vlp[0] = pass_along[0];
2565 layer_and_vlp[1] = pass_along[1];
2566 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2568 if ( !trk || !trk->name )
2571 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2572 gchar *auto_save_name = g_strdup ( trk->name );
2573 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2574 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2576 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2578 g_free ( auto_save_name );
2582 VikWaypoint *wp; // input
2583 gpointer uuid; // output
2586 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2588 wpu_udata *user_data = udata;
2589 if ( wp == user_data->wp ) {
2590 user_data->uuid = id;
2596 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2598 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2599 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2600 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2602 GTK_RESPONSE_REJECT,
2604 GTK_RESPONSE_ACCEPT,
2607 GtkWidget *label, *entry;
2608 label = gtk_label_new(_("Waypoint Name:"));
2609 entry = gtk_entry_new();
2611 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2612 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2613 gtk_widget_show_all ( label );
2614 gtk_widget_show_all ( entry );
2616 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2618 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2620 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2621 // Find *first* wp with the given name
2622 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2625 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2628 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2629 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2631 // Find and select on the side panel
2636 // Hmmm, want key of it
2637 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2639 if ( wpf && udata.uuid ) {
2640 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2641 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2650 gtk_widget_destroy ( dia );
2653 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2655 gchar *default_name = highest_wp_number_get(vtl);
2656 VikWaypoint *wp = vik_waypoint_new();
2657 gchar *returned_name;
2659 wp->coord = *def_coord;
2661 // Attempt to auto set height if DEM data is available
2662 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2663 if ( elev != VIK_DEM_INVALID_ELEVATION )
2664 wp->altitude = (gdouble)elev;
2666 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2668 if ( returned_name )
2671 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2672 g_free (default_name);
2673 g_free (returned_name);
2676 g_free (default_name);
2677 vik_waypoint_free(wp);
2681 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2684 struct LatLon one_ll, two_ll;
2685 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2687 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2688 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2689 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2690 VikViewport *vvp = vik_window_viewport(vw);
2691 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2692 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2693 vik_coord_to_latlon(&one, &one_ll);
2694 vik_coord_to_latlon(&two, &two_ll);
2695 if (one_ll.lat > two_ll.lat) {
2696 maxmin[0].lat = one_ll.lat;
2697 maxmin[1].lat = two_ll.lat;
2700 maxmin[0].lat = two_ll.lat;
2701 maxmin[1].lat = one_ll.lat;
2703 if (one_ll.lon > two_ll.lon) {
2704 maxmin[0].lon = one_ll.lon;
2705 maxmin[1].lon = two_ll.lon;
2708 maxmin[0].lon = two_ll.lon;
2709 maxmin[1].lon = one_ll.lon;
2711 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2714 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2716 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2717 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2718 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2720 trw_layer_find_maxmin (vtl, maxmin);
2721 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2724 #ifdef VIK_CONFIG_GEOTAG
2725 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2727 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2729 // Update directly - not changing the mtime
2730 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2733 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2735 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2738 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2742 * Use code in separate file for this feature as reasonably complex
2744 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2746 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2747 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2748 // Unset so can be reverified later if necessary
2749 vtl->has_verified_thumbnails = FALSE;
2751 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2757 static void trw_layer_geotagging ( gpointer lav[2] )
2759 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2760 // Unset so can be reverified later if necessary
2761 vtl->has_verified_thumbnails = FALSE;
2763 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2770 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2773 * Acquire into this TRW Layer straight from GPS Device
2775 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2777 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2778 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2779 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2780 VikViewport *vvp = vik_window_viewport(vw);
2782 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2783 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2786 #ifdef VIK_CONFIG_GOOGLE
2788 * Acquire into this TRW Layer from Google Directions
2790 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2792 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2793 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2794 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2795 VikViewport *vvp = vik_window_viewport(vw);
2797 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2801 #ifdef VIK_CONFIG_OPENSTREETMAP
2803 * Acquire into this TRW Layer from OSM
2805 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2807 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2808 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2809 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2810 VikViewport *vvp = vik_window_viewport(vw);
2812 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2816 #ifdef VIK_CONFIG_GEOCACHES
2818 * Acquire into this TRW Layer from Geocaching.com
2820 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2822 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2823 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2824 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2825 VikViewport *vvp = vik_window_viewport(vw);
2827 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2831 #ifdef VIK_CONFIG_GEOTAG
2833 * Acquire into this TRW Layer from images
2835 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2837 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2838 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2839 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2840 VikViewport *vvp = vik_window_viewport(vw);
2842 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2843 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2845 // Reverify thumbnails as they may have changed
2846 vtl->has_verified_thumbnails = FALSE;
2847 trw_layer_verify_thumbnails ( vtl, NULL );
2851 static void trw_layer_gps_upload ( gpointer lav[2] )
2853 gpointer pass_along[6];
2854 pass_along[0] = lav[0];
2855 pass_along[1] = lav[1];
2856 pass_along[2] = NULL; // No track - operate on the layer
2857 pass_along[3] = NULL;
2858 pass_along[4] = NULL;
2859 pass_along[5] = NULL;
2861 trw_layer_gps_upload_any ( pass_along );
2865 * If pass_along[3] is defined that this will upload just that track
2867 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
2869 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2870 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
2872 // May not actually get a track here as pass_along[3] can be null
2873 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2875 gboolean on_track = track ? TRUE : FALSE;
2877 if (on_track && !track->visible) {
2878 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
2882 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
2883 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2884 GTK_DIALOG_DESTROY_WITH_PARENT,
2886 GTK_RESPONSE_ACCEPT,
2888 GTK_RESPONSE_REJECT,
2891 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2892 GtkWidget *response_w = NULL;
2893 #if GTK_CHECK_VERSION (2, 20, 0)
2894 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2898 gtk_widget_grab_focus ( response_w );
2900 gpointer dgs = datasource_gps_setup ( dialog, on_track );
2902 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2903 datasource_gps_clean_up ( dgs );
2904 gtk_widget_destroy ( dialog );
2908 // Get info from reused datasource dialog widgets
2909 gchar* protocol = datasource_gps_get_protocol ( dgs );
2910 gchar* port = datasource_gps_get_descriptor ( dgs );
2911 // NB don't free the above strings as they're references to values held elsewhere
2912 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
2913 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
2914 gboolean turn_off = datasource_gps_get_off ( dgs );
2916 gtk_widget_destroy ( dialog );
2918 // When called from the viewport - work the corresponding layerspanel:
2920 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
2923 // Apply settings to transfer to the GPS device
2930 vik_layers_panel_get_viewport (vlp),
2938 * Acquire into this TRW Layer from any GPS Babel supported file
2940 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
2942 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2943 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2944 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2945 VikViewport *vvp = vik_window_viewport(vw);
2947 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
2950 static void trw_layer_new_wp ( gpointer lav[2] )
2952 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2953 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2954 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2955 instead return true if you want to update. */
2956 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 )
2957 vik_layers_panel_emit_update ( vlp );
2960 static void trw_layer_new_track ( gpointer lav[2] )
2962 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2964 if ( ! vtl->current_track ) {
2965 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
2966 vtl->current_track = vik_track_new();
2967 vtl->current_track->visible = TRUE;
2968 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
2970 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
2974 static void trw_layer_finish_track ( gpointer lav[2] )
2976 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2977 vtl->current_track = NULL;
2978 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2981 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2983 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2984 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2986 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2987 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2988 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2989 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2990 vik_layers_panel_emit_update ( vlp );
2994 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2996 /* NB do not care if wp is visible or not */
2997 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3000 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3002 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3003 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3005 /* Only 1 waypoint - jump straight to it */
3006 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3007 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3008 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3010 /* If at least 2 waypoints - find center and then zoom to fit */
3011 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3013 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3014 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3015 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3018 vik_layers_panel_emit_update ( vlp );
3021 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3023 static gpointer pass_along[2];
3025 GtkWidget *export_submenu;
3026 pass_along[0] = vtl;
3027 pass_along[1] = vlp;
3029 item = gtk_menu_item_new();
3030 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3031 gtk_widget_show ( item );
3033 if ( vtl->current_track ) {
3034 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3036 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3037 gtk_widget_show ( item );
3040 item = gtk_menu_item_new ();
3041 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3042 gtk_widget_show ( item );
3045 /* Now with icons */
3046 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3047 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3049 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3050 gtk_widget_show ( item );
3052 GtkWidget *view_submenu = gtk_menu_new();
3053 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3054 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3055 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3056 gtk_widget_show ( item );
3057 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3059 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3061 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3062 gtk_widget_show ( item );
3064 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3065 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3066 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3067 gtk_widget_show ( item );
3069 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3070 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3072 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3073 gtk_widget_show ( item );
3075 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3077 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3078 gtk_widget_show ( item );
3080 export_submenu = gtk_menu_new ();
3081 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3082 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3083 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3084 gtk_widget_show ( item );
3085 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3087 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3089 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3090 gtk_widget_show ( item );
3092 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3094 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3095 gtk_widget_show ( item );
3097 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3098 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3099 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3100 gtk_widget_show ( item );
3102 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3104 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3105 gtk_widget_show ( item );
3107 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3108 item = gtk_menu_item_new_with_mnemonic ( external1 );
3109 g_free ( external1 );
3110 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3111 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3112 gtk_widget_show ( item );
3114 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3115 item = gtk_menu_item_new_with_mnemonic ( external2 );
3116 g_free ( external2 );
3117 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3118 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3119 gtk_widget_show ( item );
3121 GtkWidget *new_submenu = gtk_menu_new();
3122 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3123 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3124 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3125 gtk_widget_show(item);
3126 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3128 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3129 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3131 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3132 gtk_widget_show ( item );
3134 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3135 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3137 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3138 gtk_widget_show ( item );
3139 // Make it available only when a new track *not* already in progress
3140 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3142 #ifdef VIK_CONFIG_GEONAMES
3143 GtkWidget *wikipedia_submenu = gtk_menu_new();
3144 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3145 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3146 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3147 gtk_widget_show(item);
3148 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3150 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3151 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3153 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3154 gtk_widget_show ( item );
3156 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3159 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3160 gtk_widget_show ( item );
3163 #ifdef VIK_CONFIG_GEOTAG
3164 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3165 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3166 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3167 gtk_widget_show ( item );
3170 GtkWidget *acquire_submenu = gtk_menu_new ();
3171 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3172 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3173 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3174 gtk_widget_show ( item );
3175 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3177 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3179 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3180 gtk_widget_show ( item );
3182 #ifdef VIK_CONFIG_GOOGLE
3183 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3185 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3186 gtk_widget_show ( item );
3189 #ifdef VIK_CONFIG_OPENSTREETMAP
3190 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3191 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3192 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3193 gtk_widget_show ( item );
3196 #ifdef VIK_CONFIG_GEOCACHES
3197 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3198 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3199 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3200 gtk_widget_show ( item );
3203 #ifdef VIK_CONFIG_GEOTAG
3204 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3205 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3206 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3207 gtk_widget_show ( item );
3210 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3211 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3212 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3213 gtk_widget_show ( item );
3215 GtkWidget *upload_submenu = gtk_menu_new ();
3216 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3217 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3218 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3219 gtk_widget_show ( item );
3220 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3222 #ifdef VIK_CONFIG_OPENSTREETMAP
3223 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3224 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3225 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3226 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3227 gtk_widget_show ( item );
3230 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3231 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3233 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3234 gtk_widget_show ( item );
3236 GtkWidget *delete_submenu = gtk_menu_new ();
3237 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3238 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3239 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3240 gtk_widget_show ( item );
3241 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3243 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3244 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3246 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3247 gtk_widget_show ( item );
3249 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3250 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3251 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3252 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3253 gtk_widget_show ( item );
3255 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3256 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3257 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3258 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3259 gtk_widget_show ( item );
3261 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3262 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3264 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3265 gtk_widget_show ( item );
3267 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3268 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3270 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3271 gtk_widget_show ( item );
3274 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3275 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3277 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3278 gtk_widget_show ( item );
3282 // Fake Waypoint UUIDs vi simple increasing integer
3283 static guint wp_uuid = 0;
3285 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3289 vik_waypoint_set_name (wp, name);
3291 if ( VIK_LAYER(vtl)->realized )
3293 // Do we need to create the sublayer:
3294 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3295 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3298 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3300 // Visibility column always needed for waypoints
3301 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3302 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 );
3304 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 );
3306 // Actual setting of visibility dependent on the waypoint
3307 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3309 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3312 highest_wp_number_add_wp(vtl, name);
3313 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3317 // Fake Track UUIDs vi simple increasing integer
3318 static guint tr_uuid = 0;
3320 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3324 vik_track_set_name (t, name);
3326 if ( VIK_LAYER(vtl)->realized )
3328 // Do we need to create the sublayer:
3329 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3330 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3333 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3334 // Visibility column always needed for tracks
3335 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3336 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 );
3338 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 );
3340 // Actual setting of visibility dependent on the track
3341 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3343 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3346 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3350 /* to be called whenever a track has been deleted or may have been changed. */
3351 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3353 if (vtl->current_tp_track == trk )
3354 trw_layer_cancel_current_tp ( vtl, FALSE );
3357 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3360 gchar *newname = g_strdup(name);
3361 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
3362 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
3363 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3365 newname = new_newname;
3371 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3373 // No more uniqueness of name forced when loading from a file
3374 // This now makes this function a little redunant as we just flow the parameters through
3375 vik_trw_layer_add_waypoint ( vtl, name, wp );
3378 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3380 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3381 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3382 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3383 vik_track_free ( tr );
3384 vtl->route_finder_append = FALSE; /* this means we have added it */
3387 // No more uniqueness of name forced when loading from a file
3388 vik_trw_layer_add_track ( vtl, name, tr );
3390 if ( vtl->route_finder_check_added_track ) {
3391 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3392 vtl->route_finder_added_track = tr;
3397 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3399 *l = g_list_append(*l, id);
3403 * Move an item from one TRW layer to another TRW layer
3405 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3407 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3408 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3410 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3412 VikTrack *trk2 = vik_track_copy ( trk );
3413 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3414 vik_trw_layer_delete_track ( vtl_src, trk );
3417 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3418 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3420 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3422 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3423 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3424 trw_layer_delete_waypoint ( vtl_src, wp );
3428 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3430 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3431 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3433 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3434 GList *items = NULL;
3437 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3438 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3440 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3441 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3446 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3447 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3449 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3456 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3457 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3462 VikTrack *trk; // input
3463 gpointer uuid; // output
3466 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3468 trku_udata *user_data = udata;
3469 if ( trk == user_data->trk ) {
3470 user_data->uuid = id;
3476 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3478 gboolean was_visible = FALSE;
3480 if ( trk && trk->name ) {
3482 if ( trk == vtl->current_track ) {
3483 vtl->current_track = NULL;
3484 vtl->current_tp_track = NULL;
3485 vtl->current_tp_id = NULL;
3486 vtl->moving_tp = FALSE;
3489 was_visible = trk->visible;
3491 if ( trk == vtl->route_finder_current_track )
3492 vtl->route_finder_current_track = NULL;
3494 if ( trk == vtl->route_finder_added_track )
3495 vtl->route_finder_added_track = NULL;
3501 // Hmmm, want key of it
3502 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3504 if ( trkf && udata.uuid ) {
3505 /* could be current_tp, so we have to check */
3506 trw_layer_cancel_tps_of_track ( vtl, trk );
3508 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3511 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3512 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3513 g_hash_table_remove ( vtl->tracks, udata.uuid );
3515 // If last sublayer, then remove sublayer container
3516 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3517 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3525 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3527 gboolean was_visible = FALSE;
3529 if ( wp && wp->name ) {
3531 if ( wp == vtl->current_wp ) {
3532 vtl->current_wp = NULL;
3533 vtl->current_wp_id = NULL;
3534 vtl->moving_wp = FALSE;
3537 was_visible = wp->visible;
3543 // Hmmm, want key of it
3544 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3546 if ( wpf && udata.uuid ) {
3547 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3550 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3551 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3553 highest_wp_number_remove_wp(vtl, wp->name);
3554 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3556 // If last sublayer, then remove sublayer container
3557 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3558 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3568 // Only for temporary use by trw_layer_delete_waypoint_by_name
3569 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3571 wpu_udata *user_data = udata;
3572 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3573 user_data->uuid = id;
3580 * Delete a waypoint by the given name
3581 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3582 * as there be multiple waypoints with the same name
3584 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3587 // Fake a waypoint with the given name
3588 udata.wp = vik_waypoint_new ();
3589 vik_waypoint_set_name (udata.wp, name);
3590 // Currently only the name is used in this waypoint find function
3593 // Hmmm, want key of it
3594 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3596 vik_waypoint_free (udata.wp);
3598 if ( wpf && udata.uuid )
3599 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3605 VikTrack *trk; // input
3606 gpointer uuid; // output
3609 // Only for temporary use by trw_layer_delete_track_by_name
3610 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3612 tpu_udata *user_data = udata;
3613 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3614 user_data->uuid = id;
3621 * Delete a track by the given name
3622 * NOTE: ATM this will delete the first encountered Track with the specified name
3623 * as there be multiple track with the same name
3625 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3628 // Fake a track with the given name
3629 udata.trk = vik_track_new ();
3630 vik_track_set_name (udata.trk, name);
3631 // Currently only the name is used in this waypoint find function
3634 // Hmmm, want key of it
3635 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3637 vik_track_free (udata.trk);
3639 if ( trkf && udata.uuid )
3640 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3645 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3647 vik_treeview_item_delete (vt, it );
3650 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3653 vtl->current_track = NULL;
3654 vtl->route_finder_current_track = NULL;
3655 vtl->route_finder_added_track = NULL;
3656 if (vtl->current_tp_track)
3657 trw_layer_cancel_current_tp(vtl, FALSE);
3659 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3660 g_hash_table_remove_all(vtl->tracks_iters);
3661 g_hash_table_remove_all(vtl->tracks);
3663 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3665 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3668 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3670 vtl->current_wp = NULL;
3671 vtl->current_wp_id = NULL;
3672 vtl->moving_wp = FALSE;
3674 highest_wp_number_reset(vtl);
3676 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3677 g_hash_table_remove_all(vtl->waypoints_iters);
3678 g_hash_table_remove_all(vtl->waypoints);
3680 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3682 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3685 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3687 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3688 // Get confirmation from the user
3689 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3690 _("Are you sure you want to delete all tracks in %s?"),
3691 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3692 vik_trw_layer_delete_all_tracks (vtl);
3695 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3697 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3698 // Get confirmation from the user
3699 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3700 _("Are you sure you want to delete all waypoints in %s?"),
3701 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3702 vik_trw_layer_delete_all_waypoints (vtl);
3705 static void trw_layer_delete_item ( gpointer pass_along[6] )
3707 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3708 gboolean was_visible = FALSE;
3709 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3711 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3712 if ( wp && wp->name ) {
3713 if ( GPOINTER_TO_INT ( pass_along[4]) )
3714 // Get confirmation from the user
3715 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3716 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3717 _("Are you sure you want to delete the waypoint \"%s\""),
3720 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3725 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3726 if ( trk && trk->name ) {
3727 if ( GPOINTER_TO_INT ( pass_along[4]) )
3728 // Get confirmation from the user
3729 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3730 _("Are you sure you want to delete the track \"%s\""),
3733 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3737 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3741 static void trw_layer_properties_item ( gpointer pass_along[7] )
3743 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3744 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3746 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3748 if ( wp && wp->name )
3750 gboolean updated = FALSE;
3751 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3753 if ( updated && VIK_LAYER(vtl)->visible )
3754 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3759 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3760 if ( tr && tr->name )
3762 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3764 pass_along[1], /* vlp */
3765 pass_along[5] ); /* vvp */
3771 Parameter 1 -> VikLayersPanel
3772 Parameter 2 -> VikLayer
3773 Parameter 3 -> VikViewport
3775 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3778 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3779 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3782 /* since vlp not set, vl & vvp should be valid instead! */
3784 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3785 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3790 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3792 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3793 if ( trps && trps->data )
3794 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3797 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3799 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3800 if ( track && track->trackpoints )
3802 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3804 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
3805 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3806 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3807 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3808 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3812 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3814 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3815 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3817 vtl->current_track = track;
3818 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3820 if ( track->trackpoints )
3821 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3824 #ifdef VIK_CONFIG_GOOGLE
3826 * extend a track using route finder
3828 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3830 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3831 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3832 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3834 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3835 vtl->route_finder_coord = last_coord;
3836 vtl->route_finder_current_track = track;
3837 vtl->route_finder_started = TRUE;
3839 if ( track->trackpoints )
3840 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3845 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3847 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3848 /* Also warn if overwrite old elevation data */
3849 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3851 vik_track_apply_dem_data ( track );
3854 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3856 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3859 trps = g_list_last(trps);
3860 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3863 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3865 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3868 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3871 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3873 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3876 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3879 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3881 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3884 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3888 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3890 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3892 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3893 if ( trk && trk->trackpoints )
3895 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3896 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
3897 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3898 if ( pass_along[1] )
3899 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3901 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3905 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3907 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3908 trw_layer_tpwin_init ( vtl );
3911 /*************************************
3912 * merge/split by time routines
3913 *************************************/
3915 /* called for each key in track hash table.
3916 * If the current track has the same time stamp type, add it to the result,
3917 * except the one pointed by "exclude".
3918 * set exclude to NULL if there is no exclude to check.
3919 * Note that the result is in reverse (for performance reasons).
3924 gboolean with_timestamps;
3926 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3928 twt_udata *user_data = udata;
3929 VikTrackpoint *p1, *p2;
3931 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3935 if (VIK_TRACK(value)->trackpoints) {
3936 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3937 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3939 if ( user_data->with_timestamps ) {
3940 if (!p1->has_timestamp || !p2->has_timestamp) {
3945 // Don't add tracks with timestamps when getting non timestamp tracks
3946 if (p1->has_timestamp || p2->has_timestamp) {
3952 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3955 /* called for each key in track hash table. if original track user_data[1] is close enough
3956 * to the passed one, add it to list in user_data[0]
3958 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
3961 VikTrackpoint *p1, *p2;
3962 VikTrack *trk = VIK_TRACK(value);
3964 GList **nearby_tracks = ((gpointer *)user_data)[0];
3965 GList *tpoints = ((gpointer *)user_data)[1];
3968 * detect reasons for not merging, and return
3969 * if no reason is found not to merge, then do it.
3972 // Exclude the original track from the compiled list
3973 if (trk->trackpoints == tpoints) {
3977 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
3978 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
3980 if (trk->trackpoints) {
3981 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
3982 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
3984 if (!p1->has_timestamp || !p2->has_timestamp) {
3985 //g_print("no timestamp\n");
3989 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3990 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
3991 if (! (abs(t1 - p2->timestamp) < threshold ||
3993 abs(p1->timestamp - t2) < threshold)
4000 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4003 /* comparison function used to sort tracks; a and b are hash table keys */
4004 /* Not actively used - can be restored if needed
4005 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4007 GHashTable *tracks = user_data;
4010 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4011 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4013 if (t1 < t2) return -1;
4014 if (t1 > t2) return 1;
4019 /* comparison function used to sort trackpoints */
4020 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4022 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4024 if (t1 < t2) return -1;
4025 if (t1 > t2) return 1;
4030 * comparison function which can be used to sort tracks or waypoints by name
4032 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4034 const gchar* namea = (const gchar*) a;
4035 const gchar* nameb = (const gchar*) b;
4036 if ( namea == NULL || nameb == NULL)
4039 // Same sort method as used in the vik_treeview_*_alphabetize functions
4040 return strcmp ( namea, nameb );
4044 * Attempt to merge selected track with other tracks specified by the user
4045 * Tracks to merge with must be of the same 'type' as the selected track -
4046 * either all with timestamps, or all without timestamps
4048 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4050 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4051 GList *other_tracks = NULL;
4052 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4054 if ( !track->trackpoints )
4058 udata.result = &other_tracks;
4059 udata.exclude = track->trackpoints;
4060 // Allow merging with 'similar' time type time tracks
4061 // i.e. either those times, or those without
4062 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4064 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4065 other_tracks = g_list_reverse(other_tracks);
4067 if ( !other_tracks ) {
4068 if ( udata.with_timestamps )
4069 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4071 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4075 // Sort alphabetically for user presentation
4076 // Convert into list of names for usage with dialog function
4077 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4078 GList *other_tracks_names = NULL;
4079 GList *iter = g_list_first ( other_tracks );
4081 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
4082 iter = g_list_next ( iter );
4085 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4087 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4088 other_tracks_names, TRUE,
4089 _("Merge with..."), _("Select track to merge with"));
4090 g_list_free(other_tracks);
4091 g_list_free(other_tracks_names);
4096 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4097 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
4099 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4100 merge_track->trackpoints = NULL;
4101 vik_trw_layer_delete_track (vtl, merge_track);
4102 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4105 /* TODO: free data before free merge_list */
4106 for (l = merge_list; l != NULL; l = g_list_next(l))
4108 g_list_free(merge_list);
4109 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4113 // c.f. trw_layer_sorted_track_id_by_name_list
4114 // but don't add the specified track to the list (normally current track)
4115 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4117 twt_udata *user_data = udata;
4120 if (trk->trackpoints == user_data->exclude) {
4124 // Sort named list alphabetically
4125 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4129 * Join - this allows combining 'routes' and 'tracks'
4130 * i.e. doesn't care about whether tracks have consistent timestamps
4131 * ATM can only append one track at a time to the currently selected track
4133 static void trw_layer_append_track ( gpointer pass_along[6] )
4136 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4137 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4139 GList *other_tracks_names = NULL;
4141 // Sort alphabetically for user presentation
4142 // Convert into list of names for usage with dialog function
4143 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4145 udata.result = &other_tracks_names;
4146 udata.exclude = trk->trackpoints;
4148 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4150 // Note the limit to selecting one track only
4151 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4152 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4153 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4157 _("Select the track to append after the current track"));
4159 g_list_free(other_tracks_names);
4161 // It's a list, but shouldn't contain more than one other track!
4162 if ( append_list ) {
4164 for (l = append_list; l != NULL; l = g_list_next(l)) {
4165 // TODO: at present this uses the first track found by name,
4166 // which with potential multiple same named tracks may not be the one selected...
4167 VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data );
4168 if ( append_track ) {
4169 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4170 append_track->trackpoints = NULL;
4171 vik_trw_layer_delete_track (vtl, append_track);
4174 for (l = append_list; l != NULL; l = g_list_next(l))
4176 g_list_free(append_list);
4177 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4181 /* merge by segments */
4182 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4184 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4185 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4186 guint segments = vik_track_merge_segments ( trk );
4187 // NB currently no need to redraw as segments not actually shown on the display
4188 // However inform the user of what happened:
4190 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4191 g_snprintf(str, 64, tmp_str, segments);
4192 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4195 /* merge by time routine */
4196 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4198 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4202 GList *tracks_with_timestamp = NULL;
4203 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4204 if (orig_trk->trackpoints &&
4205 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4206 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4211 udata.result = &tracks_with_timestamp;
4212 udata.exclude = orig_trk->trackpoints;
4213 udata.with_timestamps = TRUE;
4214 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4215 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4217 if (!tracks_with_timestamp) {
4218 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4221 g_list_free(tracks_with_timestamp);
4223 static guint threshold_in_minutes = 1;
4224 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4225 _("Merge Threshold..."),
4226 _("Merge when time between tracks less than:"),
4227 &threshold_in_minutes)) {
4231 // keep attempting to merge all tracks until no merges within the time specified is possible
4232 gboolean attempt_merge = TRUE;
4233 GList *nearby_tracks = NULL;
4235 static gpointer params[3];
4237 while ( attempt_merge ) {
4239 // Don't try again unless tracks have changed
4240 attempt_merge = FALSE;
4242 trps = orig_trk->trackpoints;
4246 if (nearby_tracks) {
4247 g_list_free(nearby_tracks);
4248 nearby_tracks = NULL;
4251 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4252 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4254 /* g_print("Original track times: %d and %d\n", t1, t2); */
4255 params[0] = &nearby_tracks;
4256 params[1] = (gpointer)trps;
4257 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4259 /* get a list of adjacent-in-time tracks */
4260 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4263 GList *l = nearby_tracks;
4266 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4267 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4269 t1 = get_first_trackpoint(l)->timestamp;
4270 t2 = get_last_trackpoint(l)->timestamp;
4271 #undef get_first_trackpoint
4272 #undef get_last_trackpoint
4273 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4276 /* remove trackpoints from merged track, delete track */
4277 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4278 VIK_TRACK(l->data)->trackpoints = NULL;
4279 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4281 // Tracks have changed, therefore retry again against all the remaining tracks
4282 attempt_merge = TRUE;
4287 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
4290 g_list_free(nearby_tracks);
4291 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4295 * Split a track at the currently selected trackpoint
4297 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl )
4299 if ( !vtl->current_tpl )
4302 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
4303 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4305 VikTrack *tr = vik_track_new ();
4306 GList *newglist = g_list_alloc ();
4307 newglist->prev = NULL;
4308 newglist->next = vtl->current_tpl->next;
4309 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4310 tr->trackpoints = newglist;
4312 vtl->current_tpl->next->prev = newglist; /* end old track here */
4313 vtl->current_tpl->next = NULL;
4315 vtl->current_tpl = newglist; /* change tp to first of new track. */
4316 vtl->current_tp_track = tr;
4320 vik_trw_layer_add_track ( vtl, name, tr );
4326 // Also need id of newly created track
4327 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4328 if ( trkf && udata.uuid )
4329 vtl->current_tp_id = udata.uuid;
4331 vtl->current_tp_id = NULL;
4333 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4338 /* split by time routine */
4339 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
4341 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4342 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4343 GList *trps = track->trackpoints;
4345 GList *newlists = NULL;
4346 GList *newtps = NULL;
4347 static guint thr = 1;
4354 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4355 _("Split Threshold..."),
4356 _("Split when time between trackpoints exceeds:"),
4361 /* iterate through trackpoints, and copy them into new lists without touching original list */
4362 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
4366 ts = VIK_TRACKPOINT(iter->data)->timestamp;
4368 g_print("panic: ts < prev_ts: this should never happen!\n");
4371 if (ts - prev_ts > thr*60) {
4372 /* flush accumulated trackpoints into new list */
4373 newlists = g_list_append(newlists, g_list_reverse(newtps));
4377 /* accumulate trackpoint copies in newtps, in reverse order */
4378 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4380 iter = g_list_next(iter);
4383 newlists = g_list_append(newlists, g_list_reverse(newtps));
4386 /* put lists of trackpoints into tracks */
4388 // Only bother updating if the split results in new tracks
4389 if (g_list_length (newlists) > 1) {
4394 tr = vik_track_new();
4395 tr->visible = track->visible;
4396 tr->trackpoints = (GList *)(iter->data);
4398 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4399 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4400 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
4401 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
4403 iter = g_list_next(iter);
4405 // Remove original track and then update the display
4406 vik_trw_layer_delete_track (vtl, track);
4407 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4409 g_list_free(newlists);
4413 * Split a track by the number of points as specified by the user
4415 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
4417 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4418 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4420 // Check valid track
4421 GList *trps = track->trackpoints;
4425 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4426 _("Split Every Nth Point"),
4427 _("Split on every Nth point:"),
4428 250, // Default value as per typical limited track capacity of various GPS devices
4432 // Was a valid number returned?
4438 GList *newlists = NULL;
4439 GList *newtps = NULL;
4444 /* accumulate trackpoint copies in newtps, in reverse order */
4445 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4447 if (count >= points) {
4448 /* flush accumulated trackpoints into new list */
4449 newlists = g_list_append(newlists, g_list_reverse(newtps));
4453 iter = g_list_next(iter);
4456 // If there is a remaining chunk put that into the new split list
4457 // This may well be the whole track if no split points were encountered
4459 newlists = g_list_append(newlists, g_list_reverse(newtps));
4462 /* put lists of trackpoints into tracks */
4464 // Only bother updating if the split results in new tracks
4465 if (g_list_length (newlists) > 1) {
4470 tr = vik_track_new();
4471 tr->visible = track->visible;
4472 tr->trackpoints = (GList *)(iter->data);
4474 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4475 vik_trw_layer_add_track(vtl, new_tr_name, tr);
4477 iter = g_list_next(iter);
4479 // Remove original track and then update the display
4480 vik_trw_layer_delete_track (vtl, track);
4481 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4483 g_list_free(newlists);
4487 * Split a track at the currently selected trackpoint
4489 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
4491 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4492 trw_layer_split_at_selected_trackpoint ( vtl );
4496 * Split a track by its segments
4498 static void trw_layer_split_segments ( gpointer pass_along[6] )
4500 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4501 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4504 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
4507 for ( i = 0; i < ntracks; i++ ) {
4509 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
4510 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
4515 // Remove original track
4516 vik_trw_layer_delete_track ( vtl, trk );
4517 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4520 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
4523 /* end of split/merge routines */
4526 * Delete adjacent track points at the same position
4527 * AKA Delete Dulplicates on the Properties Window
4529 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
4531 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4532 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4534 gulong removed = vik_track_remove_dup_points ( trk );
4536 // Track has been updated so update tps:
4537 trw_layer_cancel_tps_of_track ( vtl, trk );
4539 // Inform user how much was deleted as it's not obvious from the normal view
4541 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4542 g_snprintf(str, 64, tmp_str, removed);
4543 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4545 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4549 * Delete adjacent track points with the same timestamp
4550 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
4552 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
4554 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4555 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4557 gulong removed = vik_track_remove_same_time_points ( trk );
4559 // Track has been updated so update tps:
4560 trw_layer_cancel_tps_of_track ( vtl, trk );
4562 // Inform user how much was deleted as it's not obvious from the normal view
4564 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4565 g_snprintf(str, 64, tmp_str, removed);
4566 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4568 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4574 static void trw_layer_reverse ( gpointer pass_along[6] )
4576 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4577 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4579 // Check valid track
4580 GList *trps = track->trackpoints;
4584 vik_track_reverse ( track );
4586 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4590 * Similar to trw_layer_enum_item, but this uses a sorted method
4593 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4595 GList **list = (GList**)udata;
4596 // *list = g_list_prepend(*all, key); //unsorted method
4597 // Sort named list alphabetically
4598 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4603 * Now Waypoint specific sort
4605 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4607 GList **list = (GList**)udata;
4608 // Sort named list alphabetically
4609 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4613 * Track specific sort
4615 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4617 GList **list = (GList**)udata;
4618 // Sort named list alphabetically
4619 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4624 gboolean has_same_track_name;
4625 const gchar *same_track_name;
4626 } same_track_name_udata;
4628 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4630 const gchar* namea = (const gchar*) aa;
4631 const gchar* nameb = (const gchar*) bb;
4634 gint result = strcmp ( namea, nameb );
4636 if ( result == 0 ) {
4637 // Found two names the same
4638 same_track_name_udata *user_data = udata;
4639 user_data->has_same_track_name = TRUE;
4640 user_data->same_track_name = namea;
4643 // Leave ordering the same
4648 * Find out if any tracks have the same name in this layer
4650 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4652 // Sort items by name, then compare if any next to each other are the same
4654 GList *track_names = NULL;
4655 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4658 if ( ! track_names )
4661 same_track_name_udata udata;
4662 udata.has_same_track_name = FALSE;
4664 // Use sort routine to traverse list comparing items
4665 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4666 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4667 // Still no tracks...
4671 return udata.has_same_track_name;
4675 * Force unqiue track names for this layer
4676 * Note the panel is a required parameter to enable the update of the names displayed
4678 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4680 // . Search list for an instance of repeated name
4681 // . get track of this name
4682 // . create new name
4683 // . rename track & update equiv. treeview iter
4684 // . repeat until all different
4686 same_track_name_udata udata;
4688 GList *track_names = NULL;
4689 udata.has_same_track_name = FALSE;
4690 udata.same_track_name = NULL;
4692 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4695 if ( ! track_names )
4698 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4700 // Still no tracks...
4701 if ( ! dummy_list1 )
4704 while ( udata.has_same_track_name ) {
4706 // Find a track with the same name
4707 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4711 g_critical("Houston, we've had a problem.");
4712 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4713 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4718 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4719 vik_track_set_name ( trk, newname );
4725 // Need want key of it for treeview update
4726 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4728 if ( trkf && udataU.uuid ) {
4730 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4733 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4734 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4735 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4740 // Start trying to find same names again...
4742 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4743 udata.has_same_track_name = FALSE;
4744 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4746 // No tracks any more - give up searching
4747 if ( ! dummy_list2 )
4748 udata.has_same_track_name = FALSE;
4752 vik_layers_panel_emit_update ( vlp );
4758 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4760 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4763 // Ensure list of track names offered is unique
4764 if ( trw_layer_has_same_track_names ( vtl ) ) {
4765 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4766 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4767 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4773 // Sort list alphabetically for better presentation
4774 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4777 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4781 // Get list of items to delete from the user
4782 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4785 _("Delete Selection"),
4786 _("Select tracks to delete"));
4789 // Delete requested tracks
4790 // since specificly requested, IMHO no need for extra confirmation
4791 if ( delete_list ) {
4793 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4794 // This deletes first trk it finds of that name (but uniqueness is enforced above)
4795 trw_layer_delete_track_by_name (vtl, l->data);
4797 g_list_free(delete_list);
4798 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4803 gboolean has_same_waypoint_name;
4804 const gchar *same_waypoint_name;
4805 } same_waypoint_name_udata;
4807 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4809 const gchar* namea = (const gchar*) aa;
4810 const gchar* nameb = (const gchar*) bb;
4813 gint result = strcmp ( namea, nameb );
4815 if ( result == 0 ) {
4816 // Found two names the same
4817 same_waypoint_name_udata *user_data = udata;
4818 user_data->has_same_waypoint_name = TRUE;
4819 user_data->same_waypoint_name = namea;
4822 // Leave ordering the same
4827 * Find out if any waypoints have the same name in this layer
4829 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
4831 // Sort items by name, then compare if any next to each other are the same
4833 GList *waypoint_names = NULL;
4834 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4837 if ( ! waypoint_names )
4840 same_waypoint_name_udata udata;
4841 udata.has_same_waypoint_name = FALSE;
4843 // Use sort routine to traverse list comparing items
4844 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4845 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4846 // Still no waypoints...
4850 return udata.has_same_waypoint_name;
4854 * Force unqiue waypoint names for this layer
4855 * Note the panel is a required parameter to enable the update of the names displayed
4857 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4859 // . Search list for an instance of repeated name
4860 // . get waypoint of this name
4861 // . create new name
4862 // . rename waypoint & update equiv. treeview iter
4863 // . repeat until all different
4865 same_waypoint_name_udata udata;
4867 GList *waypoint_names = NULL;
4868 udata.has_same_waypoint_name = FALSE;
4869 udata.same_waypoint_name = NULL;
4871 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4874 if ( ! waypoint_names )
4877 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4879 // Still no waypoints...
4880 if ( ! dummy_list1 )
4883 while ( udata.has_same_waypoint_name ) {
4885 // Find a waypoint with the same name
4886 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
4890 g_critical("Houston, we've had a problem.");
4891 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4892 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
4897 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
4898 vik_waypoint_set_name ( waypoint, newname );
4901 udataU.wp = waypoint;
4904 // Need want key of it for treeview update
4905 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4907 if ( wpf && udataU.uuid ) {
4909 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4912 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4913 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4914 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4919 // Start trying to find same names again...
4920 waypoint_names = NULL;
4921 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4922 udata.has_same_waypoint_name = FALSE;
4923 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4925 // No waypoints any more - give up searching
4926 if ( ! dummy_list2 )
4927 udata.has_same_waypoint_name = FALSE;
4931 vik_layers_panel_emit_update ( vlp );
4937 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4939 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4942 // Ensure list of waypoint names offered is unique
4943 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
4944 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4945 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4946 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4952 // Sort list alphabetically for better presentation
4953 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4955 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4959 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4961 // Get list of items to delete from the user
4962 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4965 _("Delete Selection"),
4966 _("Select waypoints to delete"));
4969 // Delete requested waypoints
4970 // since specificly requested, IMHO no need for extra confirmation
4971 if ( delete_list ) {
4973 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4974 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
4975 trw_layer_delete_waypoint_by_name (vtl, l->data);
4977 g_list_free(delete_list);
4978 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4983 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4985 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4987 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4990 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4992 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4993 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4997 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4999 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5001 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
5003 // No actual change to the name supplied
5004 if (strcmp(newname, wp->name) == 0 )
5007 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5010 // An existing waypoint has been found with the requested name
5011 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5012 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
5017 // Update WP name and refresh the treeview
5018 vik_waypoint_set_name (wp, newname);
5020 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5021 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5024 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5029 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5031 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
5033 // No actual change to the name supplied
5034 if (strcmp(newname, trk->name) == 0)
5037 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
5040 // An existing track has been found with the requested name
5041 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5042 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
5046 // Update track name and refresh GUI parts
5047 vik_track_set_name (trk, newname);
5049 // Update any subwindows that could be displaying this track which has changed name
5050 // Only one Track Edit Window
5051 if ( l->current_tp_track == trk && l->tpwin ) {
5052 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5054 // Property Dialog of the track
5055 vik_trw_layer_propwin_update ( trk );
5057 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5058 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5061 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5068 static gboolean is_valid_geocache_name ( gchar *str )
5070 gint len = strlen ( str );
5071 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]));
5074 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
5076 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5077 a_acquire_set_filter_track ( trk );
5080 #ifdef VIK_CONFIG_GOOGLE
5081 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
5083 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
5084 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
5087 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
5089 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5091 gchar *escaped = uri_escape ( tr->comment );
5092 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
5093 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5100 /* vlp can be NULL if necessary - i.e. right-click from a tool */
5101 /* viewpoint is now available instead */
5102 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
5104 static gpointer pass_along[8];
5106 gboolean rv = FALSE;
5109 pass_along[1] = vlp;
5110 pass_along[2] = GINT_TO_POINTER (subtype);
5111 pass_along[3] = sublayer;
5112 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5113 pass_along[5] = vvp;
5114 pass_along[6] = iter;
5115 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
5117 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5121 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5122 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5123 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5124 gtk_widget_show ( item );
5126 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5127 VikTrwLayer *vtl = l;
5128 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
5129 if (tr && tr->property_dialog)
5130 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5133 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5134 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5135 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5136 gtk_widget_show ( item );
5138 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
5139 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
5140 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5141 gtk_widget_show ( item );
5143 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
5144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
5145 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5146 gtk_widget_show ( item );
5148 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5150 gboolean separator_created = FALSE;
5152 /* could be a right-click using the tool */
5153 if ( vlp != NULL ) {
5154 item = gtk_menu_item_new ();
5155 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5156 gtk_widget_show ( item );
5158 separator_created = TRUE;
5160 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5161 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
5163 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5164 gtk_widget_show ( item );
5167 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5169 if ( wp && wp->name ) {
5170 if ( is_valid_geocache_name ( wp->name ) ) {
5172 if ( !separator_created ) {
5173 item = gtk_menu_item_new ();
5174 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5175 gtk_widget_show ( item );
5176 separator_created = TRUE;
5179 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
5180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
5181 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5182 gtk_widget_show ( item );
5186 if ( wp && wp->image )
5188 if ( !separator_created ) {
5189 item = gtk_menu_item_new ();
5190 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5191 gtk_widget_show ( item );
5192 separator_created = TRUE;
5195 // Set up image paramater
5196 pass_along[5] = wp->image;
5198 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
5199 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
5200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
5201 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5202 gtk_widget_show ( item );
5204 #ifdef VIK_CONFIG_GEOTAG
5205 GtkWidget *geotag_submenu = gtk_menu_new ();
5206 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
5207 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
5208 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5209 gtk_widget_show ( item );
5210 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
5212 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
5213 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
5214 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5215 gtk_widget_show ( item );
5217 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
5218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
5219 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5220 gtk_widget_show ( item );
5227 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
5230 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
5231 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
5233 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5234 gtk_widget_show ( item );
5237 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
5239 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
5240 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5241 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
5242 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5243 gtk_widget_show ( item );
5245 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
5246 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5247 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
5248 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5249 gtk_widget_show ( item );
5251 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
5252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
5254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5255 gtk_widget_show ( item );
5257 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
5258 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5259 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
5260 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5261 gtk_widget_show ( item );
5264 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
5268 if ( l->current_track ) {
5269 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
5270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
5271 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5272 gtk_widget_show ( item );
5274 item = gtk_menu_item_new ();
5275 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5276 gtk_widget_show ( item );
5279 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
5280 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5281 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
5282 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5283 gtk_widget_show ( item );
5285 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
5286 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5287 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
5288 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5289 gtk_widget_show ( item );
5290 // Make it available only when a new track *not* already in progress
5291 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
5293 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
5294 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
5295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
5296 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5297 gtk_widget_show ( item );
5299 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
5300 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5301 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
5302 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5303 gtk_widget_show ( item );
5306 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5308 item = gtk_menu_item_new ();
5309 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5310 gtk_widget_show ( item );
5312 if ( l->current_track ) {
5313 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
5314 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
5315 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5316 gtk_widget_show ( item );
5319 item = gtk_menu_item_new ();
5320 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5321 gtk_widget_show ( item );
5324 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
5325 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5326 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
5327 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5328 gtk_widget_show ( item );
5330 GtkWidget *goto_submenu;
5331 goto_submenu = gtk_menu_new ();
5332 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5333 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5334 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5335 gtk_widget_show ( item );
5336 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
5338 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
5339 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
5340 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
5341 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5342 gtk_widget_show ( item );
5344 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
5345 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
5346 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
5347 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5348 gtk_widget_show ( item );
5350 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
5351 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
5352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
5353 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5354 gtk_widget_show ( item );
5356 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
5357 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
5358 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
5359 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5360 gtk_widget_show ( item );
5362 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
5363 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
5364 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
5365 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5366 gtk_widget_show ( item );
5368 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
5369 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
5370 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
5371 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5372 gtk_widget_show ( item );
5374 GtkWidget *combine_submenu;
5375 combine_submenu = gtk_menu_new ();
5376 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
5377 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
5378 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5379 gtk_widget_show ( item );
5380 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
5382 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
5383 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
5384 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5385 gtk_widget_show ( item );
5387 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
5388 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
5389 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5390 gtk_widget_show ( item );
5392 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
5393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
5394 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5395 gtk_widget_show ( item );
5397 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
5398 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
5399 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5400 gtk_widget_show ( item );
5402 GtkWidget *split_submenu;
5403 split_submenu = gtk_menu_new ();
5404 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
5405 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
5406 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5407 gtk_widget_show ( item );
5408 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
5410 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
5411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
5412 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5413 gtk_widget_show ( item );
5415 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
5416 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
5417 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5418 gtk_widget_show ( item );
5420 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
5421 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
5422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
5423 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5424 gtk_widget_show ( item );
5426 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
5427 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
5428 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5429 gtk_widget_show ( item );
5430 // Make it available only when a trackpoint is selected.
5431 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
5433 GtkWidget *delete_submenu;
5434 delete_submenu = gtk_menu_new ();
5435 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
5436 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
5437 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5438 gtk_widget_show ( item );
5439 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
5441 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
5442 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
5443 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5444 gtk_widget_show ( item );
5446 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
5447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
5448 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5449 gtk_widget_show ( item );
5451 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
5452 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
5453 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
5454 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5455 gtk_widget_show ( item );
5457 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
5459 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
5460 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
5461 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
5462 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5463 gtk_widget_show ( item );
5466 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
5467 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
5468 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
5469 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5470 gtk_widget_show ( item );
5472 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
5473 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
5474 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
5475 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5476 gtk_widget_show ( item );
5478 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
5479 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
5480 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
5481 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5482 gtk_widget_show ( item );
5484 #ifdef VIK_CONFIG_GOOGLE
5485 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
5486 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
5487 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
5488 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5489 gtk_widget_show ( item );
5492 GtkWidget *upload_submenu = gtk_menu_new ();
5493 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
5494 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5495 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5496 gtk_widget_show ( item );
5497 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
5499 #ifdef VIK_CONFIG_OPENSTREETMAP
5500 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
5501 // Convert internal pointer into actual track for usage outside this file
5502 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
5503 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
5505 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5506 gtk_widget_show ( item );
5509 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
5510 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
5511 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
5512 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5513 gtk_widget_show ( item );
5515 #ifdef VIK_CONFIG_GOOGLE
5516 if ( is_valid_google_route ( l, sublayer ) )
5518 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
5519 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
5520 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
5521 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5522 gtk_widget_show ( item );
5526 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
5527 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
5528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
5529 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5530 gtk_widget_show ( item );
5532 /* ATM This function is only available via the layers panel, due to needing a vlp */
5534 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
5535 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
5536 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
5538 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5539 gtk_widget_show ( item );
5543 #ifdef VIK_CONFIG_GEOTAG
5544 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
5545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
5546 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5547 gtk_widget_show ( item );
5550 // Only show on viewport popmenu when a trackpoint is selected
5551 if ( ! vlp && l->current_tpl ) {
5553 item = gtk_menu_item_new ();
5554 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5555 gtk_widget_show ( item );
5557 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
5558 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
5559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
5560 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5561 gtk_widget_show ( item );
5569 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
5572 if (!vtl->current_tpl)
5574 if (!vtl->current_tpl->next)
5577 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
5578 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
5580 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
5583 VikTrackpoint *tp_new = vik_trackpoint_new();
5584 struct LatLon ll_current, ll_next;
5585 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
5586 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
5588 /* main positional interpolation */
5589 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
5590 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
5592 /* Now other properties that can be interpolated */
5593 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
5595 if (tp_current->has_timestamp && tp_next->has_timestamp) {
5596 /* Note here the division is applied to each part, then added
5597 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
5598 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
5599 tp_new->has_timestamp = TRUE;
5602 if (tp_current->speed != NAN && tp_next->speed != NAN)
5603 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
5605 /* TODO - improve interpolation of course, as it may not be correct.
5606 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
5607 [similar applies if value is in radians] */
5608 if (tp_current->course != NAN && tp_next->course != NAN)
5609 tp_new->speed = (tp_current->course + tp_next->course)/2;
5611 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
5613 /* Insert new point into the trackpoints list after the current TP */
5614 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5615 gint index = g_list_index ( tr->trackpoints, tp_current );
5617 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
5622 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
5628 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
5632 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
5634 if ( vtl->current_tpl )
5636 vtl->current_tpl = NULL;
5637 vtl->current_tp_track = NULL;
5638 vtl->current_tp_id = NULL;
5639 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5643 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
5645 g_assert ( vtl->tpwin != NULL );
5646 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
5647 trw_layer_cancel_current_tp ( vtl, TRUE );
5649 if ( vtl->current_tpl == NULL )
5652 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
5654 trw_layer_split_at_selected_trackpoint ( vtl );
5655 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5657 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5659 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5665 // Find available adjacent trackpoint
5666 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5668 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5669 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5671 // Delete current trackpoint
5672 vik_trackpoint_free ( vtl->current_tpl->data );
5673 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5675 // Set to current to the available adjacent trackpoint
5676 vtl->current_tpl = new_tpl;
5678 // Reset dialog with the available adjacent trackpoint
5679 if ( vtl->current_tp_track )
5680 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
5682 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5686 // Delete current trackpoint
5687 vik_trackpoint_free ( vtl->current_tpl->data );
5688 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
5689 trw_layer_cancel_current_tp ( vtl, FALSE );
5692 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5694 if ( vtl->current_tp_track )
5695 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
5696 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
5698 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5700 if ( vtl->current_tp_track )
5701 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
5702 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5704 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5706 trw_layer_insert_tp_after_current_tp ( vtl );
5707 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5709 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
5710 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5713 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5717 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5718 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5719 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5720 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5721 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5723 if ( vtl->current_tpl )
5724 if ( vtl->current_tp_track )
5725 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5726 /* set layer name and TP data */
5729 /***************************************************************************
5731 ***************************************************************************/
5733 /*** Utility data structures and functions ****/
5737 gint closest_x, closest_y;
5738 gpointer *closest_wp_id;
5739 VikWaypoint *closest_wp;
5745 gint closest_x, closest_y;
5746 gpointer closest_track_id;
5747 VikTrackpoint *closest_tp;
5752 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5758 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5760 // If waypoint has an image then use the image size to select
5762 gint slackx, slacky;
5763 slackx = wp->image_width / 2;
5764 slacky = wp->image_height / 2;
5766 if ( x <= params->x + slackx && x >= params->x - slackx
5767 && y <= params->y + slacky && y >= params->y - slacky ) {
5768 params->closest_wp_id = id;
5769 params->closest_wp = wp;
5770 params->closest_x = x;
5771 params->closest_y = y;
5774 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5775 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5776 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5778 params->closest_wp_id = id;
5779 params->closest_wp = wp;
5780 params->closest_x = x;
5781 params->closest_y = y;
5785 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5787 GList *tpl = t->trackpoints;
5796 tp = VIK_TRACKPOINT(tpl->data);
5798 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5800 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5801 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5802 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5804 params->closest_track_id = id;
5805 params->closest_tp = tp;
5806 params->closest_tpl = tpl;
5807 params->closest_x = x;
5808 params->closest_y = y;
5814 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5816 TPSearchParams params;
5820 params.closest_track_id = NULL;
5821 params.closest_tp = NULL;
5822 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5823 return params.closest_tp;
5826 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5828 WPSearchParams params;
5832 params.closest_wp = NULL;
5833 params.closest_wp_id = NULL;
5834 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5835 return params.closest_wp;
5839 // Some forward declarations
5840 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5841 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5842 static void marker_end_move ( tool_ed_t *t );
5845 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5849 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5851 // Here always allow snapping back to the original location
5852 // this is useful when one decides not to move the thing afterall
5853 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5856 if ( event->state & GDK_CONTROL_MASK )
5858 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5860 new_coord = tp->coord;
5864 if ( event->state & GDK_SHIFT_MASK )
5866 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5868 new_coord = wp->coord;
5872 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5874 marker_moveto ( t, x, y );
5881 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5883 if ( t->holding && event->button == 1 )
5886 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5889 if ( event->state & GDK_CONTROL_MASK )
5891 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5893 new_coord = tp->coord;
5897 if ( event->state & GDK_SHIFT_MASK )
5899 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5901 new_coord = wp->coord;
5904 marker_end_move ( t );
5906 // Determine if working on a waypoint or a trackpoint
5907 if ( t->is_waypoint )
5908 vtl->current_wp->coord = new_coord;
5910 if ( vtl->current_tpl ) {
5911 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5914 if ( vtl->current_tp_track )
5915 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5920 vtl->current_wp = NULL;
5921 vtl->current_wp_id = NULL;
5922 trw_layer_cancel_current_tp ( vtl, FALSE );
5924 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5931 Returns true if a waypoint or track is found near the requested event position for this particular layer
5932 The item found is automatically selected
5933 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5935 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5937 if ( event->button != 1 )
5940 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5943 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5946 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5948 if (vtl->waypoints_visible) {
5949 WPSearchParams wp_params;
5950 wp_params.vvp = vvp;
5951 wp_params.x = event->x;
5952 wp_params.y = event->y;
5953 wp_params.closest_wp_id = NULL;
5954 wp_params.closest_wp = NULL;
5956 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5958 if ( wp_params.closest_wp ) {
5961 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5963 // Too easy to move it so must be holding shift to start immediately moving it
5964 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5965 if ( event->state & GDK_SHIFT_MASK ||
5966 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5967 // Put into 'move buffer'
5968 // NB vvp & vw already set in tet
5969 tet->vtl = (gpointer)vtl;
5970 tet->is_waypoint = TRUE;
5972 marker_begin_move (tet, event->x, event->y);
5975 vtl->current_wp = wp_params.closest_wp;
5976 vtl->current_wp_id = wp_params.closest_wp_id;
5978 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5984 if (vtl->tracks_visible) {
5985 TPSearchParams tp_params;
5986 tp_params.vvp = vvp;
5987 tp_params.x = event->x;
5988 tp_params.y = event->y;
5989 tp_params.closest_track_id = NULL;
5990 tp_params.closest_tp = NULL;
5992 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5994 if ( tp_params.closest_tp ) {
5996 // Always select + highlight the track
5997 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5999 tet->is_waypoint = FALSE;
6001 // Select the Trackpoint
6002 // Can move it immediately when control held or it's the previously selected tp
6003 if ( event->state & GDK_CONTROL_MASK ||
6004 vtl->current_tpl == tp_params.closest_tpl ) {
6005 // Put into 'move buffer'
6006 // NB vvp & vw already set in tet
6007 tet->vtl = (gpointer)vtl;
6008 marker_begin_move (tet, event->x, event->y);
6011 vtl->current_tpl = tp_params.closest_tpl;
6012 vtl->current_tp_id = tp_params.closest_track_id;
6013 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
6015 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
6018 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6020 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6025 /* these aren't the droids you're looking for */
6026 vtl->current_wp = NULL;
6027 vtl->current_wp_id = NULL;
6028 trw_layer_cancel_current_tp ( vtl, FALSE );
6031 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
6036 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6038 if ( event->button != 3 )
6041 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6044 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
6047 /* Post menu for the currently selected item */
6049 /* See if a track is selected */
6050 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6051 if ( track && track->visible ) {
6053 if ( track->name ) {
6055 if ( vtl->track_right_click_menu )
6056 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
6058 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
6064 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6066 if ( trkf && udataU.uuid ) {
6068 GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6070 trw_layer_sublayer_add_menu_items ( vtl,
6071 vtl->track_right_click_menu,
6073 VIK_TRW_LAYER_SUBLAYER_TRACK,
6079 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6085 /* See if a waypoint is selected */
6086 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6087 if ( waypoint && waypoint->visible ) {
6088 if ( waypoint->name ) {
6090 if ( vtl->wp_right_click_menu )
6091 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
6093 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
6096 udata.wp = waypoint;
6099 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
6101 if ( wpf && udata.uuid ) {
6102 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
6104 trw_layer_sublayer_add_menu_items ( vtl,
6105 vtl->wp_right_click_menu,
6107 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
6112 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6121 /* background drawing hook, to be passed the viewport */
6122 static gboolean tool_sync_done = TRUE;
6124 static gboolean tool_sync(gpointer data)
6126 VikViewport *vvp = data;
6127 gdk_threads_enter();
6128 vik_viewport_sync(vvp);
6129 tool_sync_done = TRUE;
6130 gdk_threads_leave();
6134 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
6137 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
6138 gdk_gc_set_function ( t->gc, GDK_INVERT );
6139 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6140 vik_viewport_sync(t->vvp);
6145 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
6147 VikViewport *vvp = t->vvp;
6148 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6149 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6153 if (tool_sync_done) {
6154 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
6155 tool_sync_done = FALSE;
6159 static void marker_end_move ( tool_ed_t *t )
6161 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6162 g_object_unref ( t->gc );
6166 /*** Edit waypoint ****/
6168 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6170 tool_ed_t *t = g_new(tool_ed_t, 1);
6176 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6178 WPSearchParams params;
6179 tool_ed_t *t = data;
6180 VikViewport *vvp = t->vvp;
6182 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6189 if ( !vtl->vl.visible || !vtl->waypoints_visible )
6192 if ( vtl->current_wp && vtl->current_wp->visible )
6194 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
6196 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
6198 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
6199 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
6201 if ( event->button == 3 )
6202 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6204 marker_begin_move(t, event->x, event->y);
6211 params.x = event->x;
6212 params.y = event->y;
6213 params.closest_wp_id = NULL;
6214 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6215 params.closest_wp = NULL;
6216 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6217 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
6219 // how do we get here?
6220 marker_begin_move(t, event->x, event->y);
6221 g_critical("shouldn't be here");
6224 else if ( params.closest_wp )
6226 if ( event->button == 3 )
6227 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6229 vtl->waypoint_rightclick = FALSE;
6231 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
6233 vtl->current_wp = params.closest_wp;
6234 vtl->current_wp_id = params.closest_wp_id;
6236 /* could make it so don't update if old WP is off screen and new is null but oh well */
6237 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6241 vtl->current_wp = NULL;
6242 vtl->current_wp_id = NULL;
6243 vtl->waypoint_rightclick = FALSE;
6244 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6248 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6250 tool_ed_t *t = data;
6251 VikViewport *vvp = t->vvp;
6253 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6258 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6261 if ( event->state & GDK_CONTROL_MASK )
6263 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6265 new_coord = tp->coord;
6269 if ( event->state & GDK_SHIFT_MASK )
6271 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6272 if ( wp && wp != vtl->current_wp )
6273 new_coord = wp->coord;
6278 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6280 marker_moveto ( t, x, y );
6287 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6289 tool_ed_t *t = data;
6290 VikViewport *vvp = t->vvp;
6292 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6295 if ( t->holding && event->button == 1 )
6298 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6301 if ( event->state & GDK_CONTROL_MASK )
6303 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6305 new_coord = tp->coord;
6309 if ( event->state & GDK_SHIFT_MASK )
6311 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6312 if ( wp && wp != vtl->current_wp )
6313 new_coord = wp->coord;
6316 marker_end_move ( t );
6318 vtl->current_wp->coord = new_coord;
6319 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6322 /* PUT IN RIGHT PLACE!!! */
6323 if ( event->button == 3 && vtl->waypoint_rightclick )
6325 if ( vtl->wp_right_click_menu )
6326 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
6327 if ( vtl->current_wp ) {
6328 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
6329 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 );
6330 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6332 vtl->waypoint_rightclick = FALSE;
6337 /**** Begin track ***/
6338 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
6343 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6345 vtl->current_track = NULL;
6346 return tool_new_track_click ( vtl, event, vvp );
6349 /*** New track ****/
6351 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
6358 GdkDrawable *drawable;
6364 * Draw specified pixmap
6366 static gboolean draw_sync ( gpointer data )
6368 draw_sync_t *ds = (draw_sync_t*) data;
6369 // Sometimes don't want to draw
6370 // normally because another update has taken precedent such as panning the display
6371 // which means this pixmap is no longer valid
6372 if ( ds->vtl->draw_sync_do ) {
6373 gdk_threads_enter();
6374 gdk_draw_drawable (ds->drawable,
6377 0, 0, 0, 0, -1, -1);
6378 ds->vtl->draw_sync_done = TRUE;
6379 gdk_threads_leave();
6384 static gchar* distance_string (gdouble distance)
6388 /* draw label with distance */
6389 vik_units_distance_t dist_units = a_vik_get_units_distance ();
6390 switch (dist_units) {
6391 case VIK_UNITS_DISTANCE_MILES:
6392 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
6393 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
6394 } else if (distance < 1609.4) {
6395 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
6397 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
6401 // VIK_UNITS_DISTANCE_KILOMETRES
6402 if (distance >= 1000 && distance < 100000) {
6403 g_sprintf(str, "%3.2f km", distance/1000.0);
6404 } else if (distance < 1000) {
6405 g_sprintf(str, "%d m", (int)distance);
6407 g_sprintf(str, "%d km", (int)distance/1000);
6411 return g_strdup (str);
6415 * Actually set the message in statusbar
6417 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
6419 // Only show elevation data when track has some elevation properties
6420 gchar str_gain_loss[64];
6421 str_gain_loss[0] = '\0';
6423 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
6424 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
6425 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
6427 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
6430 // Write with full gain/loss information
6431 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
6432 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
6437 * Figure out what information should be set in the statusbar and then write it
6439 static void update_statusbar ( VikTrwLayer *vtl )
6441 // Get elevation data
6442 gdouble elev_gain, elev_loss;
6443 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6445 /* Find out actual distance of current track */
6446 gdouble distance = vik_track_get_length (vtl->current_track);
6447 gchar *str = distance_string (distance);
6449 statusbar_write (str, elev_gain, elev_loss, vtl);
6455 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
6457 /* if we haven't sync'ed yet, we don't have time to do more. */
6458 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
6459 GList *iter = g_list_last ( vtl->current_track->trackpoints );
6460 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
6462 static GdkPixmap *pixmap = NULL;
6464 // Need to check in case window has been resized
6465 w1 = vik_viewport_get_width(vvp);
6466 h1 = vik_viewport_get_height(vvp);
6468 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6470 gdk_drawable_get_size (pixmap, &w2, &h2);
6471 if (w1 != w2 || h1 != h2) {
6472 g_object_unref ( G_OBJECT ( pixmap ) );
6473 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6476 // Reset to background
6477 gdk_draw_drawable (pixmap,
6478 vtl->current_track_newpoint_gc,
6479 vik_viewport_get_pixmap(vvp),
6480 0, 0, 0, 0, -1, -1);
6482 draw_sync_t *passalong;
6485 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
6487 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
6488 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
6489 // thus when we come to reset to the background it would include what we have already drawn!!
6490 gdk_draw_line ( pixmap,
6491 vtl->current_track_newpoint_gc,
6492 x1, y1, event->x, event->y );
6493 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
6495 /* Find out actual distance of current track */
6496 gdouble distance = vik_track_get_length (vtl->current_track);
6498 // Now add distance to where the pointer is //
6501 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
6502 vik_coord_to_latlon ( &coord, &ll );
6503 distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
6505 // Get elevation data
6506 gdouble elev_gain, elev_loss;
6507 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6509 // Adjust elevation data (if available) for the current pointer position
6511 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
6512 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
6513 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
6514 // Adjust elevation of last track point
6515 if ( elev_new > last_tpt->altitude )
6517 elev_gain += elev_new - last_tpt->altitude;
6520 elev_loss += last_tpt->altitude - elev_new;
6524 gchar *str = distance_string (distance);
6526 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
6527 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
6529 pango_layout_set_text (pl, str, -1);
6531 pango_layout_get_pixel_size ( pl, &wd, &hd );
6534 // offset from cursor a bit depending on font size
6538 // Create a background block to make the text easier to read over the background map
6539 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
6540 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
6541 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
6543 g_object_unref ( G_OBJECT ( pl ) );
6544 g_object_unref ( G_OBJECT ( background_block_gc ) );
6546 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
6547 passalong->vtl = vtl;
6548 passalong->pixmap = pixmap;
6549 passalong->drawable = GTK_WIDGET(vvp)->window;
6550 passalong->gc = vtl->current_track_newpoint_gc;
6552 // Update statusbar with full gain/loss information
6553 statusbar_write (str, elev_gain, elev_loss, vtl);
6557 // draw pixmap when we have time to
6558 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
6559 vtl->draw_sync_done = FALSE;
6560 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
6562 return VIK_LAYER_TOOL_ACK;
6565 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
6567 if ( vtl->current_track && event->keyval == GDK_Escape ) {
6568 vtl->current_track = NULL;
6569 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6571 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
6573 if ( vtl->current_track->trackpoints )
6575 GList *last = g_list_last(vtl->current_track->trackpoints);
6576 g_free ( last->data );
6577 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6580 update_statusbar ( vtl );
6582 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6588 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6592 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6595 if ( event->button == 2 ) {
6596 // As the display is panning, the new track pixmap is now invalid so don't draw it
6597 // otherwise this drawing done results in flickering back to an old image
6598 vtl->draw_sync_do = FALSE;
6602 if ( event->button == 3 && vtl->current_track )
6605 if ( vtl->current_track->trackpoints )
6607 GList *last = g_list_last(vtl->current_track->trackpoints);
6608 g_free ( last->data );
6609 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6611 update_statusbar ( vtl );
6613 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6617 if ( event->type == GDK_2BUTTON_PRESS )
6619 /* subtract last (duplicate from double click) tp then end */
6620 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
6622 GList *last = g_list_last(vtl->current_track->trackpoints);
6623 g_free ( last->data );
6624 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6625 /* undo last, then end */
6626 vtl->current_track = NULL;
6628 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6632 if ( ! vtl->current_track )
6634 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
6635 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
6637 vtl->current_track = vik_track_new();
6638 vtl->current_track->visible = TRUE;
6639 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
6641 /* incase it was created by begin track */
6642 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
6647 tp = vik_trackpoint_new();
6648 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
6650 /* snap to other TP */
6651 if ( event->state & GDK_CONTROL_MASK )
6653 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6655 tp->coord = other_tp->coord;
6658 tp->newsegment = FALSE;
6659 tp->has_timestamp = FALSE;
6661 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
6662 /* Auto attempt to get elevation from DEM data (if it's available) */
6663 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
6665 vtl->ct_x1 = vtl->ct_x2;
6666 vtl->ct_y1 = vtl->ct_y2;
6667 vtl->ct_x2 = event->x;
6668 vtl->ct_y2 = event->y;
6670 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6674 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6676 if ( event->button == 2 ) {
6677 // Pan moving ended - enable potential point drawing again
6678 vtl->draw_sync_do = TRUE;
6679 vtl->draw_sync_done = TRUE;
6683 /*** New waypoint ****/
6685 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6690 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6693 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6695 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6696 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
6697 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6702 /*** Edit trackpoint ****/
6704 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6706 tool_ed_t *t = g_new(tool_ed_t, 1);
6712 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6714 tool_ed_t *t = data;
6715 VikViewport *vvp = t->vvp;
6716 TPSearchParams params;
6717 /* OUTDATED DOCUMENTATION:
6718 find 5 pixel range on each side. then put these UTM, and a pointer
6719 to the winning track name (and maybe the winning track itself), and a
6720 pointer to the winning trackpoint, inside an array or struct. pass
6721 this along, do a foreach on the tracks which will do a foreach on the
6724 params.x = event->x;
6725 params.y = event->y;
6726 params.closest_track_id = NULL;
6727 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6728 params.closest_tp = NULL;
6730 if ( event->button != 1 )
6733 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6736 if ( !vtl->vl.visible || !vtl->tracks_visible )
6739 if ( vtl->current_tpl )
6741 /* 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.) */
6742 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6743 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6748 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6750 if ( current_tr->visible &&
6751 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
6752 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6753 marker_begin_move ( t, event->x, event->y );
6759 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6761 if ( params.closest_tp )
6763 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
6764 vtl->current_tpl = params.closest_tpl;
6765 vtl->current_tp_id = params.closest_track_id;
6766 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
6767 trw_layer_tpwin_init ( vtl );
6768 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
6769 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6773 /* these aren't the droids you're looking for */
6777 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6779 tool_ed_t *t = data;
6780 VikViewport *vvp = t->vvp;
6782 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6788 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6791 if ( event->state & GDK_CONTROL_MASK )
6793 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6794 if ( tp && tp != vtl->current_tpl->data )
6795 new_coord = tp->coord;
6797 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6800 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6801 marker_moveto ( t, x, y );
6809 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6811 tool_ed_t *t = data;
6812 VikViewport *vvp = t->vvp;
6814 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6816 if ( event->button != 1)
6821 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6824 if ( event->state & GDK_CONTROL_MASK )
6826 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6827 if ( tp && tp != vtl->current_tpl->data )
6828 new_coord = tp->coord;
6831 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6833 marker_end_move ( t );
6835 /* diff dist is diff from orig */
6837 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6839 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6846 #ifdef VIK_CONFIG_GOOGLE
6847 /*** Route Finder ***/
6848 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6853 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6856 if ( !vtl ) return FALSE;
6857 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6858 if ( event->button == 3 && vtl->route_finder_current_track ) {
6860 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6862 vtl->route_finder_coord = *new_end;
6864 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6865 /* remove last ' to:...' */
6866 if ( vtl->route_finder_current_track->comment ) {
6867 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6868 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6869 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6870 last_to - vtl->route_finder_current_track->comment - 1);
6871 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6876 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6877 struct LatLon start, end;
6878 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6879 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6882 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6883 vik_coord_to_latlon ( &(tmp), &end );
6884 vtl->route_finder_coord = tmp; /* for continuations */
6886 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6887 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6888 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6890 vtl->route_finder_check_added_track = TRUE;
6891 vtl->route_finder_started = FALSE;
6894 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6895 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6896 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6897 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6898 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6899 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
6902 /* see if anything was done -- a track was added or appended to */
6903 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6904 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 ) );
6905 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6906 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6907 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6908 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6910 vtl->route_finder_added_track = NULL;
6911 vtl->route_finder_check_added_track = FALSE;
6912 vtl->route_finder_append = FALSE;
6914 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6916 vtl->route_finder_started = TRUE;
6917 vtl->route_finder_coord = tmp;
6918 vtl->route_finder_current_track = NULL;
6924 /*** Show picture ****/
6926 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6931 /* Params are: vvp, event, last match found or NULL */
6932 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6934 if ( wp->image && wp->visible )
6936 gint x, y, slackx, slacky;
6937 GdkEventButton *event = (GdkEventButton *) params[1];
6939 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6940 slackx = wp->image_width / 2;
6941 slacky = wp->image_height / 2;
6942 if ( x <= event->x + slackx && x >= event->x - slackx
6943 && y <= event->y + slacky && y >= event->y - slacky )
6945 params[2] = wp->image; /* we've found a match. however continue searching
6946 * since we want to find the last match -- that
6947 * is, the match that was drawn last. */
6952 static void trw_layer_show_picture ( gpointer pass_along[6] )
6954 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6956 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
6959 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6960 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6961 g_free ( quoted_file );
6962 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6964 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() );
6965 g_error_free ( err );
6968 #endif /* WINDOWS */
6971 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6973 gpointer params[3] = { vvp, event, NULL };
6974 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6976 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6979 static gpointer pass_along[6];
6980 pass_along[0] = vtl;
6981 pass_along[5] = params[2];
6982 trw_layer_show_picture ( pass_along );
6983 return TRUE; /* found a match */
6986 return FALSE; /* go through other layers, searching for a match */
6989 /***************************************************************************
6991 ***************************************************************************/
6997 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6999 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
7000 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
7003 /* Structure for thumbnail creating data used in the background thread */
7005 VikTrwLayer *vtl; // Layer needed for redrawing
7006 GSList *pics; // Image list
7007 } thumbnail_create_thread_data;
7009 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
7011 guint total = g_slist_length(tctd->pics), done = 0;
7012 while ( tctd->pics )
7014 a_thumbnails_create ( (gchar *) tctd->pics->data );
7015 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
7017 return -1; /* Abort thread */
7019 tctd->pics = tctd->pics->next;
7022 // Redraw to show the thumbnails as they are now created
7023 if ( IS_VIK_LAYER(tctd->vtl) )
7024 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
7029 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
7031 while ( tctd->pics )
7033 g_free ( tctd->pics->data );
7034 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
7039 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
7041 if ( ! vtl->has_verified_thumbnails )
7043 GSList *pics = NULL;
7044 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
7047 gint len = g_slist_length ( pics );
7048 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
7049 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
7052 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7054 (vik_thr_func) create_thumbnails_thread,
7056 (vik_thr_free_func) thumbnail_create_thread_free,
7064 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
7066 return vtl->coord_mode;
7070 * Uniquify the whole layer
7071 * Also requires the layers panel as the names shown there need updating too
7072 * Returns whether the operation was successful or not
7074 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7077 vik_trw_layer_uniquify_tracks ( vtl, vlp );
7078 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
7084 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
7086 vik_coord_convert ( &(wp->coord), *dest_mode );
7089 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
7091 vik_track_convert ( tr, *dest_mode );
7094 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
7096 if ( vtl->coord_mode != dest_mode )
7098 vtl->coord_mode = dest_mode;
7099 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
7100 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
7104 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
7106 vtl->menu_selection = selection;
7109 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
7111 return (vtl->menu_selection);
7114 /* ----------- Downloading maps along tracks --------------- */
7116 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
7118 /* TODO: calculating based on current size of viewport */
7119 const gdouble w_at_zoom_0_125 = 0.0013;
7120 const gdouble h_at_zoom_0_125 = 0.0011;
7121 gdouble zoom_factor = zoom_level/0.125;
7123 wh->lat = h_at_zoom_0_125 * zoom_factor;
7124 wh->lon = w_at_zoom_0_125 * zoom_factor;
7126 return 0; /* all OK */
7129 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
7131 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
7132 (dist->lat >= ABS(to->north_south - from->north_south)))
7135 VikCoord *coord = g_malloc(sizeof(VikCoord));
7136 coord->mode = VIK_COORD_LATLON;
7138 if (ABS(gradient) < 1) {
7139 if (from->east_west > to->east_west)
7140 coord->east_west = from->east_west - dist->lon;
7142 coord->east_west = from->east_west + dist->lon;
7143 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
7145 if (from->north_south > to->north_south)
7146 coord->north_south = from->north_south - dist->lat;
7148 coord->north_south = from->north_south + dist->lat;
7149 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
7155 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
7157 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
7158 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
7160 VikCoord *next = from;
7162 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
7164 list = g_list_prepend(list, next);
7170 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
7172 typedef struct _Rect {
7177 #define GLRECT(iter) ((Rect *)((iter)->data))
7180 GList *rects_to_download = NULL;
7183 if (get_download_area_width(vvp, zoom_level, &wh))
7186 GList *iter = tr->trackpoints;
7190 gboolean new_map = TRUE;
7191 VikCoord *cur_coord, tl, br;
7194 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
7196 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7197 rect = g_malloc(sizeof(Rect));
7200 rect->center = *cur_coord;
7201 rects_to_download = g_list_prepend(rects_to_download, rect);
7206 gboolean found = FALSE;
7207 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7208 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7219 GList *fillins = NULL;
7220 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
7221 /* seems that ATM the function get_next_coord works only for LATLON */
7222 if ( cur_coord->mode == VIK_COORD_LATLON ) {
7223 /* fill-ins for far apart points */
7224 GList *cur_rect, *next_rect;
7225 for (cur_rect = rects_to_download;
7226 (next_rect = cur_rect->next) != NULL;
7227 cur_rect = cur_rect->next) {
7228 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
7229 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
7230 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
7234 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
7237 GList *iter = fillins;
7239 cur_coord = (VikCoord *)(iter->data);
7240 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7241 rect = g_malloc(sizeof(Rect));
7244 rect->center = *cur_coord;
7245 rects_to_download = g_list_prepend(rects_to_download, rect);
7250 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7251 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7255 for (iter = fillins; iter; iter = iter->next)
7257 g_list_free(fillins);
7259 if (rects_to_download) {
7260 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
7261 g_free(rect_iter->data);
7262 g_list_free(rects_to_download);
7266 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7269 gint selected_map, default_map;
7270 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
7271 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
7272 gint selected_zoom, default_zoom;
7276 VikTrwLayer *vtl = pass_along[0];
7277 VikLayersPanel *vlp = pass_along[1];
7278 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7279 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
7281 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7282 int num_maps = g_list_length(vmls);
7285 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7289 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
7290 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
7292 gchar **np = map_names;
7293 VikMapsLayer **lp = map_layers;
7294 for (i = 0; i < num_maps; i++) {
7295 gboolean dup = FALSE;
7296 vml = (VikMapsLayer *)(vmls->data);
7297 for (j = 0; j < i; j++) { /* no duplicate allowed */
7298 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
7305 *np++ = vik_maps_layer_get_map_label(vml);
7311 num_maps = lp - map_layers;
7313 for (default_map = 0; default_map < num_maps; default_map++) {
7314 /* TODO: check for parent layer's visibility */
7315 if (VIK_LAYER(map_layers[default_map])->visible)
7318 default_map = (default_map == num_maps) ? 0 : default_map;
7320 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
7321 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
7322 if (cur_zoom == zoom_vals[default_zoom])
7325 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
7327 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
7330 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7333 for (i = 0; i < num_maps; i++)
7334 g_free(map_names[i]);
7342 /**** lowest waypoint number calculation ***/
7343 static gint highest_wp_number_name_to_number(const gchar *name) {
7344 if ( strlen(name) == 3 ) {
7346 if ( n < 100 && name[0] != '0' )
7348 if ( n < 10 && name[0] != '0' )
7356 static void highest_wp_number_reset(VikTrwLayer *vtl)
7358 vtl->highest_wp_number = -1;
7361 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
7363 /* if is bigger that top, add it */
7364 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
7365 if ( new_wp_num > vtl->highest_wp_number )
7366 vtl->highest_wp_number = new_wp_num;
7369 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
7371 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
7372 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
7373 if ( vtl->highest_wp_number == old_wp_num ) {
7375 vtl->highest_wp_number--;
7377 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7378 /* search down until we find something that *does* exist */
7380 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
7381 vtl->highest_wp_number--;
7382 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7387 /* get lowest unused number */
7388 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
7391 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
7393 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
7394 return g_strdup(buf);