]> git.street.me.uk Git - andy/viking.git/blame_incremental - src/viktrwlayer.c
Update to allow C99 mode
[andy/viking.git] / src / viktrwlayer.c
... / ...
CommitLineData
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
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>
9 * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26/* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
27/* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include "viking.h"
34#include "vikmapslayer.h"
35#include "vikgpslayer.h"
36#include "viktrwlayer_tpwin.h"
37#include "viktrwlayer_propwin.h"
38#include "viktrwlayer_analysis.h"
39#include "viktrwlayer_tracklist.h"
40#include "viktrwlayer_waypointlist.h"
41#ifdef VIK_CONFIG_GEOTAG
42#include "viktrwlayer_geotag.h"
43#include "geotag_exif.h"
44#endif
45#include "garminsymbols.h"
46#include "thumbnails.h"
47#include "background.h"
48#include "gpx.h"
49#include "babel.h"
50#include "dem.h"
51#include "dems.h"
52#include "geonamessearch.h"
53#ifdef VIK_CONFIG_OPENSTREETMAP
54#include "osm-traces.h"
55#endif
56#include "acquire.h"
57#include "datasources.h"
58#include "datasource_gps.h"
59#include "vikexttool_datasources.h"
60#include "util.h"
61#include "vikutils.h"
62
63#include "vikrouting.h"
64
65#include "icons/icons.h"
66
67#ifdef HAVE_MATH_H
68#include <math.h>
69#endif
70#ifdef HAVE_STRING_H
71#include <string.h>
72#endif
73#ifdef HAVE_STDLIB_H
74#include <stdlib.h>
75#endif
76#include <stdio.h>
77#include <ctype.h>
78
79#include <gdk/gdkkeysyms.h>
80#include <glib.h>
81#include <glib/gstdio.h>
82#include <glib/gi18n.h>
83
84#define VIK_TRW_LAYER_TRACK_GC 6
85#define VIK_TRW_LAYER_TRACK_GCS 10
86#define VIK_TRW_LAYER_TRACK_GC_BLACK 0
87#define VIK_TRW_LAYER_TRACK_GC_SLOW 1
88#define VIK_TRW_LAYER_TRACK_GC_AVER 2
89#define VIK_TRW_LAYER_TRACK_GC_FAST 3
90#define VIK_TRW_LAYER_TRACK_GC_STOP 4
91#define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
92
93#define DRAWMODE_BY_TRACK 0
94#define DRAWMODE_BY_SPEED 1
95#define DRAWMODE_ALL_SAME_COLOR 2
96// Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
97// as we are (re)calculating the colour for every point
98
99#define POINTS 1
100#define LINES 2
101
102/* this is how it knows when you click if you are clicking close to a trackpoint. */
103#define TRACKPOINT_SIZE_APPROX 5
104#define WAYPOINT_SIZE_APPROX 5
105
106#define MIN_STOP_LENGTH 15
107#define MAX_STOP_LENGTH 86400
108#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
109 /* this is multiplied by user-inputted value from 1-100. */
110
111enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
112
113// See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
114typedef enum {
115 FS_XX_SMALL = 0, // 'xx-small'
116 FS_X_SMALL,
117 FS_SMALL,
118 FS_MEDIUM, // DEFAULT
119 FS_LARGE,
120 FS_X_LARGE,
121 FS_XX_LARGE,
122 FS_NUM_SIZES
123} font_size_t;
124
125struct _VikTrwLayer {
126 VikLayer vl;
127 GHashTable *tracks;
128 GHashTable *tracks_iters;
129 GHashTable *routes;
130 GHashTable *routes_iters;
131 GHashTable *waypoints_iters;
132 GHashTable *waypoints;
133 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
134 gboolean tracks_visible, routes_visible, waypoints_visible;
135 LatLonBBox waypoints_bbox;
136
137 gboolean track_draw_labels;
138 guint8 drawmode;
139 guint8 drawpoints;
140 guint8 drawpoints_size;
141 guint8 drawelevation;
142 guint8 elevation_factor;
143 guint8 drawstops;
144 guint32 stop_length;
145 guint8 drawlines;
146 guint8 drawdirections;
147 guint8 drawdirections_size;
148 guint8 line_thickness;
149 guint8 bg_line_thickness;
150 vik_layer_sort_order_t track_sort_order;
151
152 PangoLayout *tracklabellayout;
153 font_size_t track_font_size;
154 gchar *track_fsize_str;
155
156 guint8 wp_symbol;
157 guint8 wp_size;
158 gboolean wp_draw_symbols;
159 font_size_t wp_font_size;
160 gchar *wp_fsize_str;
161 vik_layer_sort_order_t wp_sort_order;
162
163 gdouble track_draw_speed_factor;
164 GArray *track_gc;
165 GdkGC *track_1color_gc;
166 GdkColor track_color;
167 GdkGC *current_track_gc;
168 // Separate GC for a track's potential new point as drawn via separate method
169 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
170 GdkGC *current_track_newpoint_gc;
171 GdkGC *track_bg_gc; GdkColor track_bg_color;
172 GdkGC *waypoint_gc; GdkColor waypoint_color;
173 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
174 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
175 gboolean wpbgand;
176 GdkFont *waypoint_font;
177 VikTrack *current_track; // ATM shared between new tracks and new routes
178 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
179 gboolean draw_sync_done;
180 gboolean draw_sync_do;
181
182 VikCoordMode coord_mode;
183
184 /* wp editing tool */
185 VikWaypoint *current_wp;
186 gpointer current_wp_id;
187 gboolean moving_wp;
188 gboolean waypoint_rightclick;
189
190 /* track editing tool */
191 GList *current_tpl;
192 VikTrack *current_tp_track;
193 gpointer current_tp_id;
194 VikTrwLayerTpwin *tpwin;
195
196 /* track editing tool -- more specifically, moving tps */
197 gboolean moving_tp;
198
199 /* route finder tool */
200 gboolean route_finder_started;
201 VikCoord route_finder_coord;
202 gboolean route_finder_check_added_track;
203 VikTrack *route_finder_added_track;
204 VikTrack *route_finder_current_track;
205 gboolean route_finder_append;
206
207 gboolean drawlabels;
208 gboolean drawimages;
209 guint8 image_alpha;
210 GQueue *image_cache;
211 guint8 image_size;
212 guint16 image_cache_size;
213
214 /* for waypoint text */
215 PangoLayout *wplabellayout;
216
217 gboolean has_verified_thumbnails;
218
219 GtkMenu *wp_right_click_menu;
220 GtkMenu *track_right_click_menu;
221
222 /* menu */
223 VikStdLayerMenuItem menu_selection;
224
225 gint highest_wp_number;
226
227 // One per layer
228 GtkWidget *tracks_analysis_dialog;
229};
230
231/* A caached waypoint image. */
232typedef struct {
233 GdkPixbuf *pixbuf;
234 gchar *image; /* filename */
235} CachedPixbuf;
236
237struct DrawingParams {
238 VikViewport *vp;
239 VikTrwLayer *vtl;
240 VikWindow *vw;
241 gdouble xmpp, ympp;
242 guint16 width, height;
243 gdouble cc; // Cosine factor in track directions
244 gdouble ss; // Sine factor in track directions
245 const VikCoord *center;
246 gboolean one_zone, lat_lon;
247 gdouble ce1, ce2, cn1, cn2;
248 LatLonBBox bbox;
249};
250
251static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
252
253typedef enum {
254 MA_VTL = 0,
255 MA_VLP,
256 MA_SUBTYPE, // OR END for Layer only
257 MA_SUBLAYER_ID,
258 MA_CONFIRM,
259 MA_VVP,
260 MA_TV_ITER,
261 MA_MISC,
262 MA_LAST,
263} menu_array_index;
264
265typedef gpointer menu_array_layer[2];
266typedef gpointer menu_array_sublayer[MA_LAST];
267
268static void trw_layer_delete_item ( menu_array_sublayer values );
269static void trw_layer_copy_item_cb ( menu_array_sublayer values );
270static void trw_layer_cut_item_cb ( menu_array_sublayer values );
271
272static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
273static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
274
275static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
276static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
277
278static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
279static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
280
281static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
282static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
283static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
284static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
285static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
286static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
287static void trw_layer_goto_track_center ( menu_array_sublayer values );
288static void trw_layer_merge_by_segment ( menu_array_sublayer values );
289static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
290static void trw_layer_merge_with_other ( menu_array_sublayer values );
291static void trw_layer_append_track ( menu_array_sublayer values );
292static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
293static void trw_layer_split_by_n_points ( menu_array_sublayer values );
294static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
295static void trw_layer_split_segments ( menu_array_sublayer values );
296static void trw_layer_delete_point_selected ( menu_array_sublayer values );
297static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
298static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
299static void trw_layer_reverse ( menu_array_sublayer values );
300static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
301static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
302static void trw_layer_show_picture ( menu_array_sublayer values );
303static void trw_layer_gps_upload_any ( menu_array_sublayer values );
304
305static void trw_layer_centerize ( menu_array_layer values );
306static void trw_layer_auto_view ( menu_array_layer values );
307static void trw_layer_export ( menu_array_layer values, const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
308static void trw_layer_goto_wp ( menu_array_layer values );
309static void trw_layer_new_wp ( menu_array_layer values );
310static void trw_layer_new_track ( menu_array_layer values );
311static void trw_layer_new_route ( menu_array_layer values );
312static void trw_layer_finish_track ( menu_array_layer values );
313static void trw_layer_auto_waypoints_view ( menu_array_layer values );
314static void trw_layer_auto_tracks_view ( menu_array_layer values );
315static void trw_layer_delete_all_tracks ( menu_array_layer values );
316static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
317static void trw_layer_delete_all_waypoints ( menu_array_layer values );
318static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
319static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
320static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
321#ifdef VIK_CONFIG_GEOTAG
322static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
323static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
324static void trw_layer_geotagging_track ( menu_array_sublayer values );
325static void trw_layer_geotagging ( menu_array_layer values );
326#endif
327static void trw_layer_acquire_gps_cb ( menu_array_layer values );
328static void trw_layer_acquire_routing_cb ( menu_array_layer values );
329static void trw_layer_acquire_url_cb ( menu_array_layer values );
330#ifdef VIK_CONFIG_OPENSTREETMAP
331static void trw_layer_acquire_osm_cb ( menu_array_layer values );
332static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
333#endif
334#ifdef VIK_CONFIG_GEOCACHES
335static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
336#endif
337#ifdef VIK_CONFIG_GEOTAG
338static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
339#endif
340static void trw_layer_acquire_file_cb ( menu_array_layer values );
341static void trw_layer_gps_upload ( menu_array_layer values );
342
343static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
344static void trw_layer_track_list_dialog ( menu_array_layer values );
345static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
346
347// Specific route versions:
348// Most track handling functions can handle operating on the route list
349// However these ones are easier in separate functions
350static void trw_layer_auto_routes_view ( menu_array_layer values );
351static void trw_layer_delete_all_routes ( menu_array_layer values );
352static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
353
354/* pop-up items */
355static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
356static void trw_layer_goto_waypoint ( menu_array_sublayer values );
357static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
358static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
359
360static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
361static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
362static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
363
364static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
365static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
366static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
367static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
368
369static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
370static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
371static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
372static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
373static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
374static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
375static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
376static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
377static void tool_edit_waypoint_destroy ( tool_ed_t *t );
378static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
379static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
380static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
381static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
382static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
383static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
384static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
385static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
386static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
387static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
388static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
389static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
391static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
392
393static void cached_pixbuf_free ( CachedPixbuf *cp );
394static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
395
396static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
397static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
398
399static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
400static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
401
402static gchar *highest_wp_number_get(VikTrwLayer *vtl);
403static void highest_wp_number_reset(VikTrwLayer *vtl);
404static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
405static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
406
407// Note for the following tool GtkRadioActionEntry texts:
408// the very first text value is an internal name not displayed anywhere
409// the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
410// * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
411// the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
412// the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
413static VikToolInterface trw_layer_tools[] = {
414 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
415 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
416 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
417 FALSE,
418 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
419
420 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
421 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
422 (VikToolMouseFunc) tool_new_track_click,
423 (VikToolMouseMoveFunc) tool_new_track_move,
424 (VikToolMouseFunc) tool_new_track_release,
425 (VikToolKeyFunc) tool_new_track_key_press,
426 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
427 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
428
429 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
430 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
431 (VikToolMouseFunc) tool_new_route_click,
432 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
433 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
434 (VikToolKeyFunc) tool_new_track_key_press, // -/#
435 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
436 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
437
438 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
439 (VikToolConstructorFunc) tool_edit_waypoint_create,
440 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
441 NULL, NULL,
442 (VikToolMouseFunc) tool_edit_waypoint_click,
443 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
444 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
445 FALSE,
446 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
447
448 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
449 (VikToolConstructorFunc) tool_edit_trackpoint_create,
450 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
451 NULL, NULL,
452 (VikToolMouseFunc) tool_edit_trackpoint_click,
453 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
454 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
455 FALSE,
456 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
457
458 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
459 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
460 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
461 FALSE,
462 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
463
464 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
465 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
466 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
467 FALSE,
468 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
469};
470
471enum {
472 TOOL_CREATE_WAYPOINT=0,
473 TOOL_CREATE_TRACK,
474 TOOL_CREATE_ROUTE,
475 TOOL_EDIT_WAYPOINT,
476 TOOL_EDIT_TRACKPOINT,
477 TOOL_SHOW_PICTURE,
478 TOOL_ROUTE_FINDER,
479 NUM_TOOLS
480};
481
482/****** PARAMETERS ******/
483
484static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced") };
485enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV };
486
487static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
488static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
489
490#define MIN_POINT_SIZE 2
491#define MAX_POINT_SIZE 10
492
493#define MIN_ARROW_SIZE 3
494#define MAX_ARROW_SIZE 20
495
496static VikLayerParamScale params_scales[] = {
497 /* min max step digits */
498 { 1, 10, 1, 0 }, /* line_thickness */
499 { 0, 100, 1, 0 }, /* track draw speed factor */
500 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
501 /* 5 * step == how much to turn */
502 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
503 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
504 { 5, 500, 5, 0 }, // 5: image cache_size - " "
505 { 0, 8, 1, 0 }, // 6: Background line thickness
506 { 1, 64, 1, 0 }, /* wpsize */
507 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
508 { 1, 100, 1, 0 }, // 9: elevation factor
509 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
510 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
511};
512
513static gchar* params_font_sizes[] = {
514 N_("Extra Extra Small"),
515 N_("Extra Small"),
516 N_("Small"),
517 N_("Medium"),
518 N_("Large"),
519 N_("Extra Large"),
520 N_("Extra Extra Large"),
521 NULL };
522
523// Needs to align with vik_layer_sort_order_t
524static gchar* params_sort_order[] = {
525 N_("None"),
526 N_("Name Ascending"),
527 N_("Name Descending"),
528 NULL
529};
530
531static VikLayerParamData black_color_default ( void ) {
532 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
533}
534static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
535static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
536static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
537static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
538static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
539static VikLayerParamData trackbgcolor_default ( void ) {
540 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
541}
542static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
543static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
544static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
545
546static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
547static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
548static VikLayerParamData wptextcolor_default ( void ) {
549 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
550}
551static VikLayerParamData wpbgcolor_default ( void ) {
552 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
553}
554static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
555static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
556
557static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
558static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
559static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
560
561static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
562
563VikLayerParam trw_layer_params[] = {
564 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
565 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
566 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
567
568 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
569 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
570 { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
571 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default, NULL, NULL },
572 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
573 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
574 { VIK_LAYER_TRW, "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
575 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
576 { VIK_LAYER_TRW, "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
577 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
578 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
579 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
580 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
581 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
582 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
583 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time"), vik_lpd_false_default, NULL, NULL },
584 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
585
586 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
587 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
588 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[1], NULL,
589 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
590 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
591
592 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
593 { VIK_LAYER_TRW, "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, wpfontsize_default, NULL, NULL },
594 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default, NULL, NULL },
595 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
596 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
597 { VIK_LAYER_TRW, "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
598 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default, NULL, NULL },
599 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[7], NULL, NULL, wpsize_default, NULL, NULL },
600 { VIK_LAYER_TRW, "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
601 { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
602
603 { VIK_LAYER_TRW, "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
604 { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[3], NULL, NULL, image_size_default, NULL, NULL },
605 { VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
606 { VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
607};
608
609// ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
610enum {
611 // Sublayer visibilities
612 PARAM_TV,
613 PARAM_WV,
614 PARAM_RV,
615 // Tracks
616 PARAM_TDL,
617 PARAM_TLFONTSIZE,
618 PARAM_DM,
619 PARAM_TC,
620 PARAM_DL,
621 PARAM_LT,
622 PARAM_DD,
623 PARAM_DDS,
624 PARAM_DP,
625 PARAM_DPS,
626 PARAM_DE,
627 PARAM_EF,
628 PARAM_DS,
629 PARAM_SL,
630 PARAM_BLT,
631 PARAM_TBGC,
632 PARAM_TDSF,
633 PARAM_TSO,
634 // Waypoints
635 PARAM_DLA,
636 PARAM_WPFONTSIZE,
637 PARAM_WPC,
638 PARAM_WPTC,
639 PARAM_WPBC,
640 PARAM_WPBA,
641 PARAM_WPSYM,
642 PARAM_WPSIZE,
643 PARAM_WPSYMS,
644 PARAM_WPSO,
645 // WP images
646 PARAM_DI,
647 PARAM_IS,
648 PARAM_IA,
649 PARAM_ICS,
650 NUM_PARAMS
651};
652
653/*** TO ADD A PARAM:
654 *** 1) Add to trw_layer_params and enumeration
655 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
656 ***/
657
658/****** END PARAMETERS ******/
659
660/* Layer Interface function definitions */
661static VikTrwLayer* trw_layer_create ( VikViewport *vp );
662static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
663static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
664static void trw_layer_free ( VikTrwLayer *trwlayer );
665static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
666static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
667static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
668static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
669static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
670static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
671static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
672static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
673static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
674static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
675static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
676static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
677static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
678static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
679static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
680static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
681static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
682static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
683static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
684static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
685static void trw_layer_free_copied_item ( gint subtype, gpointer item );
686static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
687static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
688static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
689static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
690static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
691/* End Layer Interface function definitions */
692
693VikLayerInterface vik_trw_layer_interface = {
694 "TrackWaypoint",
695 N_("TrackWaypoint"),
696 "<control><shift>Y",
697 &viktrwlayer_pixbuf,
698
699 trw_layer_tools,
700 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
701
702 trw_layer_params,
703 NUM_PARAMS,
704 params_groups, /* params_groups */
705 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
706
707 VIK_MENU_ITEM_ALL,
708
709 (VikLayerFuncCreate) trw_layer_create,
710 (VikLayerFuncRealize) trw_layer_realize,
711 (VikLayerFuncPostRead) trw_layer_post_read,
712 (VikLayerFuncFree) trw_layer_free,
713
714 (VikLayerFuncProperties) NULL,
715 (VikLayerFuncDraw) trw_layer_draw,
716 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
717
718 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
719 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
720
721 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
722 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
723
724 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
725 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
726 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
727 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
728 (VikLayerFuncLayerSelected) trw_layer_selected,
729
730 (VikLayerFuncMarshall) trw_layer_marshall,
731 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
732
733 (VikLayerFuncSetParam) trw_layer_set_param,
734 (VikLayerFuncGetParam) trw_layer_get_param,
735 (VikLayerFuncChangeParam) trw_layer_change_param,
736
737 (VikLayerFuncReadFileData) a_gpspoint_read_file,
738 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
739
740 (VikLayerFuncDeleteItem) trw_layer_del_item,
741 (VikLayerFuncCutItem) trw_layer_cut_item,
742 (VikLayerFuncCopyItem) trw_layer_copy_item,
743 (VikLayerFuncPasteItem) trw_layer_paste_item,
744 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
745
746 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
747
748 (VikLayerFuncSelectClick) trw_layer_select_click,
749 (VikLayerFuncSelectMove) trw_layer_select_move,
750 (VikLayerFuncSelectRelease) trw_layer_select_release,
751 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
752};
753
754GType vik_trw_layer_get_type ()
755{
756 static GType vtl_type = 0;
757
758 if (!vtl_type)
759 {
760 static const GTypeInfo vtl_info =
761 {
762 sizeof (VikTrwLayerClass),
763 NULL, /* base_init */
764 NULL, /* base_finalize */
765 NULL, /* class init */
766 NULL, /* class_finalize */
767 NULL, /* class_data */
768 sizeof (VikTrwLayer),
769 0,
770 NULL /* instance init */
771 };
772 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
773 }
774
775 return vtl_type;
776}
777
778static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
779{
780 static menu_array_sublayer values;
781 if (!sublayer) {
782 return;
783 }
784
785 gint ii;
786 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
787 values[ii] = NULL;
788
789 values[MA_VTL] = vtl;
790 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
791 values[MA_SUBLAYER_ID] = sublayer;
792 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
793
794 trw_layer_delete_item ( values );
795}
796
797static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
798{
799 static menu_array_sublayer values;
800 if (!sublayer) {
801 return;
802 }
803
804 gint ii;
805 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
806 values[ii] = NULL;
807
808 values[MA_VTL] = vtl;
809 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
810 values[MA_SUBLAYER_ID] = sublayer;
811 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
812
813 trw_layer_copy_item_cb(values);
814 trw_layer_cut_item_cb(values);
815}
816
817static void trw_layer_copy_item_cb ( menu_array_sublayer values)
818{
819 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
820 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
821 gpointer * sublayer = values[MA_SUBLAYER_ID];
822 guint8 *data = NULL;
823 guint len;
824
825 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
826
827 if (data) {
828 const gchar* name;
829 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
830 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
831 if ( wp && wp->name )
832 name = wp->name;
833 else
834 name = NULL; // Broken :(
835 }
836 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
837 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
838 if ( trk && trk->name )
839 name = trk->name;
840 else
841 name = NULL; // Broken :(
842 }
843 else {
844 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
845 if ( trk && trk->name )
846 name = trk->name;
847 else
848 name = NULL; // Broken :(
849 }
850
851 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
852 subtype, len, name, data);
853 }
854}
855
856static void trw_layer_cut_item_cb ( menu_array_sublayer values)
857{
858 trw_layer_copy_item_cb(values);
859 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
860 trw_layer_delete_item(values);
861}
862
863static void trw_layer_paste_item_cb ( menu_array_sublayer values)
864{
865 // Slightly cheating method, routing via the panels capability
866 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
867}
868
869static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
870{
871 guint8 *id;
872 guint il;
873
874 if (!sublayer) {
875 *item = NULL;
876 return;
877 }
878
879 GByteArray *ba = g_byte_array_new ();
880
881 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
882 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
883 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
884 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
885 } else {
886 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
887 }
888
889 g_byte_array_append ( ba, id, il );
890
891 g_free(id);
892
893 *len = ba->len;
894 *item = ba->data;
895}
896
897static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
898{
899 if ( !item )
900 return FALSE;
901
902 gchar *name;
903
904 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
905 {
906 VikWaypoint *w;
907
908 w = vik_waypoint_unmarshall ( item, len );
909 // When copying - we'll create a new name based on the original
910 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
911 vik_trw_layer_add_waypoint ( vtl, name, w );
912 waypoint_convert (NULL, w, &vtl->coord_mode);
913 g_free ( name );
914
915 trw_layer_calculate_bounds_waypoints ( vtl );
916
917 // Consider if redraw necessary for the new item
918 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
919 vik_layer_emit_update ( VIK_LAYER(vtl) );
920 return TRUE;
921 }
922 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
923 {
924 VikTrack *t;
925
926 t = vik_track_unmarshall ( item, len );
927 // When copying - we'll create a new name based on the original
928 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
929 vik_trw_layer_add_track ( vtl, name, t );
930 vik_track_convert (t, vtl->coord_mode);
931 g_free ( name );
932
933 // Consider if redraw necessary for the new item
934 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
935 vik_layer_emit_update ( VIK_LAYER(vtl) );
936 return TRUE;
937 }
938 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
939 {
940 VikTrack *t;
941
942 t = vik_track_unmarshall ( item, len );
943 // When copying - we'll create a new name based on the original
944 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
945 vik_trw_layer_add_route ( vtl, name, t );
946 vik_track_convert (t, vtl->coord_mode);
947 g_free ( name );
948
949 // Consider if redraw necessary for the new item
950 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
951 vik_layer_emit_update ( VIK_LAYER(vtl) );
952 return TRUE;
953 }
954 return FALSE;
955}
956
957static void trw_layer_free_copied_item ( gint subtype, gpointer item )
958{
959 if (item) {
960 g_free(item);
961 }
962}
963
964static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
965{
966 switch ( id )
967 {
968 case PARAM_TV: vtl->tracks_visible = data.b; break;
969 case PARAM_WV: vtl->waypoints_visible = data.b; break;
970 case PARAM_RV: vtl->routes_visible = data.b; break;
971 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
972 case PARAM_TLFONTSIZE:
973 if ( data.u < FS_NUM_SIZES ) {
974 vtl->track_font_size = data.u;
975 g_free ( vtl->track_fsize_str );
976 switch ( vtl->track_font_size ) {
977 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
978 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
979 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
980 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
981 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
982 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
983 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
984 }
985 }
986 break;
987 case PARAM_DM: vtl->drawmode = data.u; break;
988 case PARAM_TC:
989 vtl->track_color = data.c;
990 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
991 break;
992 case PARAM_DP: vtl->drawpoints = data.b; break;
993 case PARAM_DPS:
994 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
995 vtl->drawpoints_size = data.u;
996 break;
997 case PARAM_DE: vtl->drawelevation = data.b; break;
998 case PARAM_DS: vtl->drawstops = data.b; break;
999 case PARAM_DL: vtl->drawlines = data.b; break;
1000 case PARAM_DD: vtl->drawdirections = data.b; break;
1001 case PARAM_DDS:
1002 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1003 vtl->drawdirections_size = data.u;
1004 break;
1005 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1006 vtl->stop_length = data.u;
1007 break;
1008 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1009 vtl->elevation_factor = data.u;
1010 break;
1011 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1012 {
1013 vtl->line_thickness = data.u;
1014 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1015 }
1016 break;
1017 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1018 {
1019 vtl->bg_line_thickness = data.u;
1020 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1021 }
1022 break;
1023 case PARAM_TBGC:
1024 vtl->track_bg_color = data.c;
1025 if ( vtl->track_bg_gc )
1026 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1027 break;
1028 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1029 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1030 case PARAM_DLA: vtl->drawlabels = data.b; break;
1031 case PARAM_DI: vtl->drawimages = data.b; break;
1032 case PARAM_IS: if ( data.u != vtl->image_size )
1033 {
1034 vtl->image_size = data.u;
1035 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1036 g_queue_free ( vtl->image_cache );
1037 vtl->image_cache = g_queue_new ();
1038 }
1039 break;
1040 case PARAM_IA: vtl->image_alpha = data.u; break;
1041 case PARAM_ICS: vtl->image_cache_size = data.u;
1042 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1043 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1044 break;
1045 case PARAM_WPC:
1046 vtl->waypoint_color = data.c;
1047 if ( vtl->waypoint_gc )
1048 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1049 break;
1050 case PARAM_WPTC:
1051 vtl->waypoint_text_color = data.c;
1052 if ( vtl->waypoint_text_gc )
1053 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1054 break;
1055 case PARAM_WPBC:
1056 vtl->waypoint_bg_color = data.c;
1057 if ( vtl->waypoint_bg_gc )
1058 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1059 break;
1060 case PARAM_WPBA:
1061 vtl->wpbgand = data.b;
1062 if ( vtl->waypoint_bg_gc )
1063 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1064 break;
1065 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1066 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1067 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1068 case PARAM_WPFONTSIZE:
1069 if ( data.u < FS_NUM_SIZES ) {
1070 vtl->wp_font_size = data.u;
1071 g_free ( vtl->wp_fsize_str );
1072 switch ( vtl->wp_font_size ) {
1073 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1074 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1075 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1076 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1077 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1078 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1079 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1080 }
1081 }
1082 break;
1083 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1084 }
1085 return TRUE;
1086}
1087
1088static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1089{
1090 VikLayerParamData rv;
1091 switch ( id )
1092 {
1093 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1094 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1095 case PARAM_RV: rv.b = vtl->routes_visible; break;
1096 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1097 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1098 case PARAM_DM: rv.u = vtl->drawmode; break;
1099 case PARAM_TC: rv.c = vtl->track_color; break;
1100 case PARAM_DP: rv.b = vtl->drawpoints; break;
1101 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1102 case PARAM_DE: rv.b = vtl->drawelevation; break;
1103 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1104 case PARAM_DS: rv.b = vtl->drawstops; break;
1105 case PARAM_SL: rv.u = vtl->stop_length; break;
1106 case PARAM_DL: rv.b = vtl->drawlines; break;
1107 case PARAM_DD: rv.b = vtl->drawdirections; break;
1108 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1109 case PARAM_LT: rv.u = vtl->line_thickness; break;
1110 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1111 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1112 case PARAM_DI: rv.b = vtl->drawimages; break;
1113 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1114 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1115 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1116 case PARAM_IS: rv.u = vtl->image_size; break;
1117 case PARAM_IA: rv.u = vtl->image_alpha; break;
1118 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1119 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1120 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1121 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1122 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1123 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1124 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1125 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1126 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1127 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1128 }
1129 return rv;
1130}
1131
1132static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1133{
1134 // This '-3' is to account for the first few parameters not in the properties
1135 const gint OFFSET = -3;
1136
1137 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1138 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1139 case PARAM_DI: {
1140 // Get new value
1141 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1142 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1143 GtkWidget **ww2 = values[UI_CHG_LABELS];
1144 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1145 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1146 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1147 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1148 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1149 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1150 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1151 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1152 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1153 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1154 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1155 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1156 break;
1157 }
1158 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1159 case PARAM_DLA: {
1160 // Get new value
1161 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1162 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1163 GtkWidget **ww2 = values[UI_CHG_LABELS];
1164 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1165 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1166 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1167 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1168 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1169 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1170 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1171 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1172 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1173 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1174 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1175 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1176 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1177 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1178 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1179 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1180 break;
1181 }
1182 // Alter sensitivity of all track colours according to the draw track mode.
1183 case PARAM_DM: {
1184 // Get new value
1185 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1186 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1187 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1188 GtkWidget **ww2 = values[UI_CHG_LABELS];
1189 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1190 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1191 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1192 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1193 break;
1194 }
1195 // NB Since other track settings have been split across tabs,
1196 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1197 default: break;
1198 }
1199}
1200
1201static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1202{
1203 guint8 *pd;
1204 gint pl;
1205
1206 *data = NULL;
1207
1208 // Use byte arrays to store sublayer data
1209 // much like done elsewhere e.g. vik_layer_marshall_params()
1210 GByteArray *ba = g_byte_array_new ( );
1211
1212 guint8 *sl_data;
1213 guint sl_len;
1214
1215 guint object_length;
1216 guint subtype;
1217 // store:
1218 // the length of the item
1219 // the sublayer type of item
1220 // the the actual item
1221#define tlm_append(object_pointer, size, type) \
1222 subtype = (type); \
1223 object_length = (size); \
1224 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1225 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1226 g_byte_array_append ( ba, (object_pointer), object_length );
1227
1228 // Layer parameters first
1229 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1230 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1231 g_byte_array_append ( ba, pd, pl );
1232 g_free ( pd );
1233
1234 // Now sublayer data
1235 GHashTableIter iter;
1236 gpointer key, value;
1237
1238 // Waypoints
1239 g_hash_table_iter_init ( &iter, vtl->waypoints );
1240 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1241 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1242 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1243 g_free ( sl_data );
1244 }
1245
1246 // Tracks
1247 g_hash_table_iter_init ( &iter, vtl->tracks );
1248 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1249 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1250 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1251 g_free ( sl_data );
1252 }
1253
1254 // Routes
1255 g_hash_table_iter_init ( &iter, vtl->routes );
1256 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1257 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1258 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1259 g_free ( sl_data );
1260 }
1261
1262#undef tlm_append
1263
1264 *data = ba->data;
1265 *len = ba->len;
1266}
1267
1268static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1269{
1270 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1271 gint pl;
1272 gint consumed_length;
1273
1274 // First the overall layer parameters
1275 memcpy(&pl, data, sizeof(pl));
1276 data += sizeof(pl);
1277 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1278 data += pl;
1279
1280 consumed_length = pl;
1281 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1282
1283#define tlm_size (*(gint *)data)
1284 // See marshalling above for order of how this is written
1285#define tlm_next \
1286 data += sizeof_len_and_subtype + tlm_size;
1287
1288 // Now the individual sublayers:
1289
1290 while ( *data && consumed_length < len ) {
1291 // Normally four extra bytes at the end of the datastream
1292 // (since it's a GByteArray and that's where it's length is stored)
1293 // So only attempt read when there's an actual block of sublayer data
1294 if ( consumed_length + tlm_size < len ) {
1295
1296 // Reuse pl to read the subtype from the data stream
1297 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1298
1299 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1300 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1301 gchar *name = g_strdup ( trk->name );
1302 vik_trw_layer_add_track ( vtl, name, trk );
1303 g_free ( name );
1304 }
1305 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1306 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1307 gchar *name = g_strdup ( wp->name );
1308 vik_trw_layer_add_waypoint ( vtl, name, wp );
1309 g_free ( name );
1310 }
1311 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1312 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1313 gchar *name = g_strdup ( trk->name );
1314 vik_trw_layer_add_route ( vtl, name, trk );
1315 g_free ( name );
1316 }
1317 }
1318 consumed_length += tlm_size + sizeof_len_and_subtype;
1319 tlm_next;
1320 }
1321 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1322
1323 // Not stored anywhere else so need to regenerate
1324 trw_layer_calculate_bounds_waypoints ( vtl );
1325
1326 return vtl;
1327}
1328
1329// Keep interesting hash function at least visible
1330/*
1331static guint strcase_hash(gconstpointer v)
1332{
1333 // 31 bit hash function
1334 int i;
1335 const gchar *t = v;
1336 gchar s[128]; // malloc is too slow for reading big files
1337 gchar *p = s;
1338
1339 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1340 p[i] = toupper(t[i]);
1341 p[i] = '\0';
1342
1343 p = s;
1344 guint32 h = *p;
1345 if (h) {
1346 for (p += 1; *p != '\0'; p++)
1347 h = (h << 5) - h + *p;
1348 }
1349
1350 return h;
1351}
1352*/
1353
1354// Stick a 1 at the end of the function name to make it more unique
1355// thus more easily searchable in a simple text editor
1356static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1357{
1358 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1359 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1360
1361 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1362 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1363
1364 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1365 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1366 // and with normal PC processing capabilities - it has negligibile performance impact
1367 // This also minimized the amount of rework - as the management of the hash tables already exists.
1368
1369 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1370 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1371 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1372
1373 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1374 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1375 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1376 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1377 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1378 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1379
1380 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1381
1382 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1383
1384 // Param settings that are not available via the GUI
1385 // Force to on after processing params (which defaults them to off with a zero value)
1386 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1387
1388 rv->draw_sync_done = TRUE;
1389 rv->draw_sync_do = TRUE;
1390 // Everything else is 0, FALSE or NULL
1391
1392 return rv;
1393}
1394
1395
1396static void trw_layer_free ( VikTrwLayer *trwlayer )
1397{
1398 g_hash_table_destroy(trwlayer->waypoints);
1399 g_hash_table_destroy(trwlayer->waypoints_iters);
1400 g_hash_table_destroy(trwlayer->tracks);
1401 g_hash_table_destroy(trwlayer->tracks_iters);
1402 g_hash_table_destroy(trwlayer->routes);
1403 g_hash_table_destroy(trwlayer->routes_iters);
1404
1405 /* ODC: replace with GArray */
1406 trw_layer_free_track_gcs ( trwlayer );
1407
1408 if ( trwlayer->wp_right_click_menu )
1409 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1410
1411 if ( trwlayer->track_right_click_menu )
1412 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1413
1414 if ( trwlayer->tracklabellayout != NULL)
1415 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1416
1417 if ( trwlayer->wplabellayout != NULL)
1418 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1419
1420 if ( trwlayer->waypoint_gc != NULL )
1421 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1422
1423 if ( trwlayer->waypoint_text_gc != NULL )
1424 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1425
1426 if ( trwlayer->waypoint_bg_gc != NULL )
1427 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1428
1429 g_free ( trwlayer->wp_fsize_str );
1430 g_free ( trwlayer->track_fsize_str );
1431
1432 if ( trwlayer->tpwin != NULL )
1433 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1434
1435 if ( trwlayer->tracks_analysis_dialog != NULL )
1436 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1437
1438 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1439 g_queue_free ( trwlayer->image_cache );
1440}
1441
1442static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1443{
1444 dp->vtl = vtl;
1445 dp->vp = vp;
1446 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1447 dp->xmpp = vik_viewport_get_xmpp ( vp );
1448 dp->ympp = vik_viewport_get_ympp ( vp );
1449 dp->width = vik_viewport_get_width ( vp );
1450 dp->height = vik_viewport_get_height ( vp );
1451 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1452 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1453
1454 dp->center = vik_viewport_get_center ( vp );
1455 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1456 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1457
1458 if ( dp->one_zone )
1459 {
1460 gint w2, h2;
1461 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1462 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1463 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1464
1465 dp->ce1 = dp->center->east_west-w2;
1466 dp->ce2 = dp->center->east_west+w2;
1467 dp->cn1 = dp->center->north_south-h2;
1468 dp->cn2 = dp->center->north_south+h2;
1469 } else if ( dp->lat_lon ) {
1470 VikCoord upperleft, bottomright;
1471 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1472 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1473 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1474 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1475 dp->ce1 = upperleft.east_west;
1476 dp->ce2 = bottomright.east_west;
1477 dp->cn1 = bottomright.north_south;
1478 dp->cn2 = upperleft.north_south;
1479 }
1480
1481 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1482}
1483
1484/*
1485 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1486 * Here a simple traffic like light colour system is used:
1487 * . slow points are red
1488 * . average is yellow
1489 * . fast points are green
1490 */
1491static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1492{
1493 gdouble rv = 0;
1494 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1495 if ( average_speed > 0 ) {
1496 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1497 if ( rv < low_speed )
1498 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1499 else if ( rv > high_speed )
1500 return VIK_TRW_LAYER_TRACK_GC_FAST;
1501 else
1502 return VIK_TRW_LAYER_TRACK_GC_AVER;
1503 }
1504 }
1505 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1506}
1507
1508static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1509{
1510 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1511 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1512 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1513 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1514}
1515
1516
1517static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1518{
1519 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1520
1521 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1522 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1523 else
1524 // Fallback if parse failure
1525 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1526
1527 g_free ( label_markup );
1528
1529 gint label_x, label_y;
1530 gint width, height;
1531 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1532
1533 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1534 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1535}
1536
1537/**
1538 * distance_in_preferred_units:
1539 * @dist: The source distance in standard SI Units (i.e. metres)
1540 *
1541 * TODO: This is a generic function that could be moved into globals.c or utils.c
1542 *
1543 * Probably best used if you have a only few conversions to perform.
1544 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1545 * since it will be doing the preference check on each call
1546 *
1547 * Returns: The distance in the units as specified by the preferences
1548 */
1549static gdouble distance_in_preferred_units ( gdouble dist )
1550{
1551 gdouble mydist;
1552 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1553 switch (dist_units) {
1554 case VIK_UNITS_DISTANCE_MILES:
1555 mydist = VIK_METERS_TO_MILES(dist);
1556 break;
1557 // VIK_UNITS_DISTANCE_KILOMETRES:
1558 default:
1559 mydist = dist/1000.0;
1560 break;
1561 }
1562 return mydist;
1563}
1564
1565/**
1566 * trw_layer_draw_dist_labels:
1567 *
1568 * Draw a few labels along a track at nicely seperated distances
1569 * This might slow things down if there's many tracks being displayed with this on.
1570 */
1571static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1572{
1573 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1574 25.0, 40.0, 50.0, 75.0, 100.0,
1575 150.0, 200.0, 250.0, 500.0, 1000.0};
1576
1577 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1578
1579 // Convert to specified unit to find the friendly breakdown value
1580 dist = distance_in_preferred_units ( dist );
1581
1582 gint index = 0;
1583 gint i=0;
1584 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1585 if ( chunksd[i] > dist ) {
1586 index = i;
1587 dist = chunksd[index];
1588 break;
1589 }
1590 }
1591
1592 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1593
1594 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1595 gdouble dist_i = dist * i;
1596
1597 // Convert distance back into metres for use in finding a trackpoint
1598 switch (dist_units) {
1599 case VIK_UNITS_DISTANCE_MILES:
1600 dist_i = VIK_MILES_TO_METERS(dist_i);
1601 break;
1602 // VIK_UNITS_DISTANCE_KILOMETRES:
1603 default:
1604 dist_i = dist_i*1000.0;
1605 break;
1606 }
1607
1608 gdouble dist_current = 0.0;
1609 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1610 gdouble dist_next = 0.0;
1611 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1612
1613 gdouble dist_between_tps = fabs (dist_next - dist_current);
1614 gdouble ratio = 0.0;
1615 // Prevent division by 0 errors
1616 if ( dist_between_tps > 0.0 )
1617 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1618
1619 if ( tp_current && tp_next ) {
1620 // Construct the name based on the distance value
1621 gchar *name;
1622 gchar *units;
1623 switch (dist_units) {
1624 case VIK_UNITS_DISTANCE_MILES:
1625 units = g_strdup ( _("miles") );
1626 break;
1627 // VIK_UNITS_DISTANCE_KILOMETRES:
1628 default:
1629 units = g_strdup ( _("km") );
1630 break;
1631 }
1632
1633 // Convert for display
1634 dist_i = distance_in_preferred_units ( dist_i );
1635
1636 // Make the precision of the output related to the unit size.
1637 if ( index == 0 )
1638 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1639 else if ( index == 1 )
1640 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1641 else
1642 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1643 g_free ( units );
1644
1645 struct LatLon ll_current, ll_next;
1646 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1647 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1648
1649 // positional interpolation
1650 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1651 // but should be good enough over the small scale that I anticipate usage on
1652 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1653 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1654 VikCoord coord;
1655 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1656
1657 gchar *fgcolour;
1658 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1659 fgcolour = gdk_color_to_string ( &(trk->color) );
1660 else
1661 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1662
1663 // if highlight mode on, then colour the background in the highlight colour
1664 gchar *bgcolour;
1665 if ( drawing_highlight )
1666 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1667 else
1668 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1669
1670 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1671
1672 g_free ( fgcolour );
1673 g_free ( bgcolour );
1674 g_free ( name );
1675 }
1676 }
1677}
1678
1679/**
1680 * trw_layer_draw_track_name_labels:
1681 *
1682 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1683 */
1684static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1685{
1686 gchar *fgcolour;
1687 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1688 fgcolour = gdk_color_to_string ( &(trk->color) );
1689 else
1690 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1691
1692 // if highlight mode on, then colour the background in the highlight colour
1693 gchar *bgcolour;
1694 if ( drawing_highlight )
1695 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1696 else
1697 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1698
1699 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1700
1701 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1702 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1703 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1704 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1705 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1706 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1707 VikCoord coord;
1708 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1709
1710 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1711 }
1712
1713 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1714 // No other labels to draw
1715 return;
1716
1717 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1718 if ( !tp_end )
1719 return;
1720 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1721 if ( !tp_begin )
1722 return;
1723 VikCoord begin_coord = tp_begin->coord;
1724 VikCoord end_coord = tp_end->coord;
1725
1726 gboolean done_start_end = FALSE;
1727
1728 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1729 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1730
1731 // This number can be configured via the settings if you really want to change it
1732 gdouble distance_diff;
1733 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1734 distance_diff = 100.0; // Metres
1735
1736 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1737 // Start and end 'close' together so only draw one label at an average location
1738 gint x1, x2, y1, y2;
1739 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1740 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1741 VikCoord av_coord;
1742 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1743
1744 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1745 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1746 g_free ( name );
1747
1748 done_start_end = TRUE;
1749 }
1750 }
1751
1752 if ( ! done_start_end ) {
1753 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1754 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1755 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1756 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1757 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1758 g_free ( name_start );
1759 }
1760 // Don't draw end label if this is the one being created
1761 if ( trk != dp->vtl->current_track ) {
1762 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1763 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1764 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1765 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1766 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1767 g_free ( name_end );
1768 }
1769 }
1770 }
1771
1772 g_free ( fgcolour );
1773 g_free ( bgcolour );
1774 g_free ( ename );
1775}
1776
1777static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1778{
1779 if ( ! track->visible )
1780 return;
1781
1782 /* TODO: this function is a mess, get rid of any redundancy */
1783 GList *list = track->trackpoints;
1784 GdkGC *main_gc;
1785 gboolean useoldvals = TRUE;
1786
1787 gboolean drawpoints;
1788 gboolean drawstops;
1789 gboolean drawelevation;
1790 gdouble min_alt, max_alt, alt_diff = 0;
1791
1792 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1793 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1794 guint8 tp_size;
1795
1796 if ( dp->vtl->drawelevation )
1797 {
1798 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1799 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1800 alt_diff = max_alt - min_alt;
1801 }
1802
1803 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1804 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1805 trw_layer_draw_track ( id, track, dp, TRUE );
1806
1807 if ( draw_track_outline )
1808 drawpoints = drawstops = FALSE;
1809 else {
1810 drawpoints = dp->vtl->drawpoints;
1811 drawstops = dp->vtl->drawstops;
1812 }
1813
1814 gboolean drawing_highlight = FALSE;
1815 /* Current track - used for creation */
1816 if ( track == dp->vtl->current_track )
1817 main_gc = dp->vtl->current_track_gc;
1818 else {
1819 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1820 /* Draw all tracks of the layer in special colour */
1821 /* if track is member of selected layer or is the current selected track
1822 then draw in the highlight colour.
1823 NB this supercedes the drawmode */
1824 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1825 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1826 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1827 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1828 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1829 drawing_highlight = TRUE;
1830 }
1831 }
1832 if ( !drawing_highlight ) {
1833 // Still need to figure out the gc according to the drawing mode:
1834 switch ( dp->vtl->drawmode ) {
1835 case DRAWMODE_BY_TRACK:
1836 if ( dp->vtl->track_1color_gc )
1837 g_object_unref ( dp->vtl->track_1color_gc );
1838 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1839 main_gc = dp->vtl->track_1color_gc;
1840 break;
1841 default:
1842 // Mostly for DRAWMODE_ALL_SAME_COLOR
1843 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1844 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1845 break;
1846 }
1847 }
1848 }
1849
1850 if (list) {
1851 int x, y, oldx, oldy;
1852 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1853
1854 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1855
1856 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1857
1858 // Draw the first point as something a bit different from the normal points
1859 // ATM it's slightly bigger and a triangle
1860 if ( drawpoints ) {
1861 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1862 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1863 }
1864
1865 oldx = x;
1866 oldy = y;
1867
1868 gdouble average_speed = 0.0;
1869 gdouble low_speed = 0.0;
1870 gdouble high_speed = 0.0;
1871 // If necessary calculate these values - which is done only once per track redraw
1872 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1873 // the percentage factor away from the average speed determines transistions between the levels
1874 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1875 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1876 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1877 }
1878
1879 while ((list = g_list_next(list)))
1880 {
1881 tp = VIK_TRACKPOINT(list->data);
1882 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1883
1884 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1885 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1886 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1887 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1888 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1889 {
1890 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1891
1892 /*
1893 * If points are the same in display coordinates, don't draw.
1894 */
1895 if ( useoldvals && x == oldx && y == oldy )
1896 {
1897 // Still need to process points to ensure 'stops' are drawn if required
1898 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1899 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1900 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 );
1901
1902 goto skip;
1903 }
1904
1905 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1906 if ( drawpoints || dp->vtl->drawlines ) {
1907 // setup main_gc for both point and line drawing
1908 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1909 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
1910 }
1911 }
1912
1913 if ( drawpoints && ! draw_track_outline )
1914 {
1915
1916 if ( list->next ) {
1917 /*
1918 * The concept of drawing stops is that a trackpoint
1919 * that is if the next trackpoint has a timestamp far into
1920 * the future, we draw a circle of 6x trackpoint size,
1921 * instead of a rectangle of 2x trackpoint size.
1922 * This is drawn first so the trackpoint will be drawn on top
1923 */
1924 /* stops */
1925 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1926 /* Stop point. Draw 6x circle. Always in redish colour */
1927 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
1928
1929 /* Regular point - draw 2x square. */
1930 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1931 }
1932 else
1933 /* Final point - draw 4x circle. */
1934 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 );
1935 }
1936
1937 if ((!tp->newsegment) && (dp->vtl->drawlines))
1938 {
1939
1940 /* UTM only: zone check */
1941 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1942 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1943
1944 if (!useoldvals)
1945 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1946
1947 if ( draw_track_outline ) {
1948 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1949 }
1950 else {
1951
1952 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1953
1954 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1955 GdkPoint tmp[4];
1956 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1957
1958 tmp[0].x = oldx;
1959 tmp[0].y = oldy;
1960 tmp[1].x = oldx;
1961 tmp[1].y = oldy-FIXALTITUDE(list->data);
1962 tmp[2].x = x;
1963 tmp[2].y = y-FIXALTITUDE(list->next->data);
1964 tmp[3].x = x;
1965 tmp[3].y = y;
1966
1967 GdkGC *tmp_gc;
1968 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1969 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1970 else
1971 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1972 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1973
1974 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1975 }
1976 }
1977 }
1978
1979 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1980 // Draw an arrow at the mid point to show the direction of the track
1981 // Code is a rework from vikwindow::draw_ruler()
1982 gint midx = (oldx + x) / 2;
1983 gint midy = (oldy + y) / 2;
1984
1985 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1986 // Avoid divide by zero and ensure at least 1 pixel big
1987 if ( len > 1 ) {
1988 gdouble dx = (oldx - midx) / len;
1989 gdouble dy = (oldy - midy) / len;
1990 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1991 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1992 }
1993 }
1994
1995 skip:
1996 oldx = x;
1997 oldy = y;
1998 useoldvals = TRUE;
1999 }
2000 else {
2001 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2002 {
2003 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2004 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2005 {
2006 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2007
2008 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2009 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
2010 }
2011
2012 /*
2013 * If points are the same in display coordinates, don't draw.
2014 */
2015 if ( x != oldx || y != oldy )
2016 {
2017 if ( draw_track_outline )
2018 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2019 else
2020 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2021 }
2022 }
2023 else
2024 {
2025 /*
2026 * If points are the same in display coordinates, don't draw.
2027 */
2028 if ( x != oldx && y != oldy )
2029 {
2030 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2031 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2032 }
2033 }
2034 }
2035 useoldvals = FALSE;
2036 }
2037 }
2038
2039 // Labels drawn after the trackpoints, so the labels are on top
2040 if ( dp->vtl->track_draw_labels ) {
2041 if ( track->max_number_dist_labels > 0 ) {
2042 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2043 }
2044
2045 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2046 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2047 }
2048 }
2049 }
2050}
2051
2052static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2053{
2054 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2055 trw_layer_draw_track ( id, track, dp, FALSE );
2056 }
2057}
2058
2059static void cached_pixbuf_free ( CachedPixbuf *cp )
2060{
2061 g_object_unref ( G_OBJECT(cp->pixbuf) );
2062 g_free ( cp->image );
2063}
2064
2065static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2066{
2067 return strcmp ( cp->image, name );
2068}
2069
2070static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2071{
2072 if ( wp->visible )
2073 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2074 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2075 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2076 {
2077 gint x, y;
2078 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2079
2080 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2081
2082 if ( wp->image && dp->vtl->drawimages )
2083 {
2084 GdkPixbuf *pixbuf = NULL;
2085 GList *l;
2086
2087 if ( dp->vtl->image_alpha == 0)
2088 return;
2089
2090 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2091 if ( l )
2092 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2093 else
2094 {
2095 gchar *image = wp->image;
2096 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2097 if ( ! regularthumb )
2098 {
2099 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2100 image = "\x12\x00"; /* this shouldn't occur naturally. */
2101 }
2102 if ( regularthumb )
2103 {
2104 CachedPixbuf *cp = NULL;
2105 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2106 if ( dp->vtl->image_size == 128 )
2107 cp->pixbuf = regularthumb;
2108 else
2109 {
2110 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2111 g_assert ( cp->pixbuf );
2112 g_object_unref ( G_OBJECT(regularthumb) );
2113 }
2114 cp->image = g_strdup ( image );
2115
2116 /* needed so 'click picture' tool knows how big the pic is; we don't
2117 * store it in cp because they may have been freed already. */
2118 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2119 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2120
2121 g_queue_push_head ( dp->vtl->image_cache, cp );
2122 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2123 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2124
2125 pixbuf = cp->pixbuf;
2126 }
2127 else
2128 {
2129 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2130 }
2131 }
2132 if ( pixbuf )
2133 {
2134 gint w, h;
2135 w = gdk_pixbuf_get_width ( pixbuf );
2136 h = gdk_pixbuf_get_height ( pixbuf );
2137
2138 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2139 {
2140 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2141 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2142 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2143 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2144 // Highlighted - so draw a little border around the chosen one
2145 // single line seems a little weak so draw 2 of them
2146 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2147 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2148 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2149 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2150 }
2151 }
2152 if ( dp->vtl->image_alpha == 255 )
2153 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2154 else
2155 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2156 }
2157 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2158 }
2159 }
2160
2161 // Draw appropriate symbol - either symbol image or simple types
2162 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2163 vik_viewport_draw_pixbuf ( dp->vp, wp->symbol_pixbuf, 0, 0, x - gdk_pixbuf_get_width(wp->symbol_pixbuf)/2, y - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2, -1, -1 );
2164 }
2165 else if ( wp == dp->vtl->current_wp ) {
2166 switch ( dp->vtl->wp_symbol ) {
2167 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;
2168 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;
2169 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;
2170 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 );
2171 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 );
2172 }
2173 }
2174 else {
2175 switch ( dp->vtl->wp_symbol ) {
2176 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;
2177 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;
2178 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;
2179 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 );
2180 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;
2181 }
2182 }
2183
2184 if ( dp->vtl->drawlabels )
2185 {
2186 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2187 gint label_x, label_y;
2188 gint width, height;
2189 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2190
2191 // Could this stored in the waypoint rather than recreating each pass?
2192 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2193
2194 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2195 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2196 else
2197 // Fallback if parse failure
2198 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2199
2200 g_free ( wp_label_markup );
2201
2202 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2203 label_x = x - width/2;
2204 if ( wp->symbol_pixbuf )
2205 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2206 else
2207 label_y = y - dp->vtl->wp_size - height - 2;
2208
2209 /* if highlight mode on, then draw background text in highlight colour */
2210 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2211 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2212 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2213 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2214 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2215 else
2216 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2217 }
2218 else {
2219 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2220 }
2221 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2222 }
2223 }
2224}
2225
2226static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2227{
2228 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2229 trw_layer_draw_waypoint ( id, wp, dp );
2230 }
2231}
2232
2233static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2234{
2235 static struct DrawingParams dp;
2236 g_assert ( l != NULL );
2237
2238 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2239
2240 if ( l->tracks_visible )
2241 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2242
2243 if ( l->routes_visible )
2244 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2245
2246 if (l->waypoints_visible)
2247 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2248}
2249
2250static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2251{
2252 int i;
2253 if ( vtl->track_bg_gc )
2254 {
2255 g_object_unref ( vtl->track_bg_gc );
2256 vtl->track_bg_gc = NULL;
2257 }
2258 if ( vtl->track_1color_gc )
2259 {
2260 g_object_unref ( vtl->track_1color_gc );
2261 vtl->track_1color_gc = NULL;
2262 }
2263 if ( vtl->current_track_gc )
2264 {
2265 g_object_unref ( vtl->current_track_gc );
2266 vtl->current_track_gc = NULL;
2267 }
2268 if ( vtl->current_track_newpoint_gc )
2269 {
2270 g_object_unref ( vtl->current_track_newpoint_gc );
2271 vtl->current_track_newpoint_gc = NULL;
2272 }
2273
2274 if ( ! vtl->track_gc )
2275 return;
2276 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2277 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2278 g_array_free ( vtl->track_gc, TRUE );
2279 vtl->track_gc = NULL;
2280}
2281
2282static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2283{
2284 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2285 gint width = vtl->line_thickness;
2286
2287 if ( vtl->track_gc )
2288 trw_layer_free_track_gcs ( vtl );
2289
2290 if ( vtl->track_bg_gc )
2291 g_object_unref ( vtl->track_bg_gc );
2292 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2293
2294 // Ensure new track drawing heeds line thickness setting
2295 // however always have a minium of 2, as 1 pixel is really narrow
2296 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2297
2298 if ( vtl->current_track_gc )
2299 g_object_unref ( vtl->current_track_gc );
2300 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2301 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2302
2303 // 'newpoint' gc is exactly the same as the current track gc
2304 if ( vtl->current_track_newpoint_gc )
2305 g_object_unref ( vtl->current_track_newpoint_gc );
2306 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2307 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2308
2309 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2310
2311 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2312 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2313
2314 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2315 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2316 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2317
2318 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2319
2320 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2321}
2322
2323static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2324{
2325 VikTrwLayer *rv = trw_layer_new1 ( vp );
2326 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2327
2328 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2329 /* early exit, as the rest is GUI related */
2330 return rv;
2331 }
2332
2333 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2334 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2335
2336 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2337 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2338
2339 trw_layer_new_track_gcs ( rv, vp );
2340
2341 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2342 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2343 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2344 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2345
2346 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2347
2348 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2349
2350 return rv;
2351}
2352
2353#define SMALL_ICON_SIZE 18
2354/*
2355 * Can accept a null symbol, and may return null value
2356 */
2357GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2358{
2359 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2360 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2361 // So needing a small icon for the treeview may need some resizing:
2362 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2363 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2364 return wp_icon;
2365}
2366
2367static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2368{
2369 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2370
2371 GdkPixbuf *pixbuf = NULL;
2372
2373 if ( track->has_color ) {
2374 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2375 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2376 // Here is some magic found to do the conversion
2377 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2378 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2379 ((track->color.green & 0xff00) << 8) |
2380 (track->color.blue & 0xff00);
2381
2382 gdk_pixbuf_fill ( pixbuf, pixel );
2383 }
2384
2385 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
2386
2387 if ( pixbuf )
2388 g_object_unref (pixbuf);
2389
2390 *new_iter = *((GtkTreeIter *) pass_along[1]);
2391 if ( track->is_route )
2392 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2393 else
2394 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2395
2396 if ( ! track->visible )
2397 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2398}
2399
2400static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2401{
2402 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2403
2404 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
2405
2406 *new_iter = *((GtkTreeIter *) pass_along[1]);
2407 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2408
2409 if ( ! wp->visible )
2410 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2411}
2412
2413static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2414{
2415 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2416}
2417
2418static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2419{
2420 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2421}
2422
2423static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2424{
2425 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2426}
2427
2428static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2429{
2430 GtkTreeIter iter2;
2431 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2432
2433 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2434 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2435
2436 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2437
2438 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2439 }
2440
2441 if ( g_hash_table_size (vtl->routes) > 0 ) {
2442 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2443
2444 pass_along[0] = &(vtl->routes_iter);
2445 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2446
2447 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2448
2449 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2450 }
2451
2452 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2453 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2454
2455 pass_along[0] = &(vtl->waypoints_iter);
2456 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2457
2458 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2459
2460 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2461 }
2462
2463}
2464
2465static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2466{
2467 switch ( subtype )
2468 {
2469 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2470 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2471 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2472 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2473 {
2474 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2475 if (t)
2476 return (t->visible ^= 1);
2477 else
2478 return TRUE;
2479 }
2480 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2481 {
2482 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2483 if (t)
2484 return (t->visible ^= 1);
2485 else
2486 return TRUE;
2487 }
2488 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2489 {
2490 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2491 if (t)
2492 return (t->visible ^= 1);
2493 else
2494 return TRUE;
2495 }
2496 }
2497 return TRUE;
2498}
2499
2500/*
2501 * Return a property about tracks for this layer
2502 */
2503gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2504{
2505 return vtl->line_thickness;
2506}
2507
2508// Structure to hold multiple track information for a layer
2509typedef struct {
2510 gdouble length;
2511 time_t start_time;
2512 time_t end_time;
2513 gint duration;
2514} tooltip_tracks;
2515
2516/*
2517 * Build up layer multiple track information via updating the tooltip_tracks structure
2518 */
2519static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2520{
2521 tt->length = tt->length + vik_track_get_length (tr);
2522
2523 // Ensure times are available
2524 if ( tr->trackpoints &&
2525 vik_track_get_tp_first(tr)->has_timestamp &&
2526 vik_track_get_tp_last(tr)->has_timestamp ) {
2527
2528 time_t t1, t2;
2529 t1 = vik_track_get_tp_first(tr)->timestamp;
2530 t2 = vik_track_get_tp_last(tr)->timestamp;
2531
2532 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2533 // Hence initialize to the first 'proper' value
2534 if ( tt->start_time == 0 )
2535 tt->start_time = t1;
2536 if ( tt->end_time == 0 )
2537 tt->end_time = t2;
2538
2539 // Update find the earliest / last times
2540 if ( t1 < tt->start_time )
2541 tt->start_time = t1;
2542 if ( t2 > tt->end_time )
2543 tt->end_time = t2;
2544
2545 // Keep track of total time
2546 // there maybe gaps within a track (eg segments)
2547 // but this should be generally good enough for a simple indicator
2548 tt->duration = tt->duration + (int)(t2-t1);
2549 }
2550}
2551
2552/*
2553 * Generate tooltip text for the layer.
2554 * This is relatively complicated as it considers information for
2555 * no tracks, a single track or multiple tracks
2556 * (which may or may not have timing information)
2557 */
2558static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2559{
2560 gchar tbuf1[32];
2561 gchar tbuf2[64];
2562 gchar tbuf3[64];
2563 gchar tbuf4[10];
2564 tbuf1[0] = '\0';
2565 tbuf2[0] = '\0';
2566 tbuf3[0] = '\0';
2567 tbuf4[0] = '\0';
2568
2569 static gchar tmp_buf[128];
2570 tmp_buf[0] = '\0';
2571
2572 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2573
2574 // Safety check - I think these should always be valid
2575 if ( vtl->tracks && vtl->waypoints ) {
2576 tooltip_tracks tt = { 0.0, 0, 0 };
2577 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2578
2579 GDate* gdate_start = g_date_new ();
2580 g_date_set_time_t (gdate_start, tt.start_time);
2581
2582 GDate* gdate_end = g_date_new ();
2583 g_date_set_time_t (gdate_end, tt.end_time);
2584
2585 if ( g_date_compare (gdate_start, gdate_end) ) {
2586 // Dates differ so print range on separate line
2587 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2588 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2589 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2590 }
2591 else {
2592 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2593 if ( tt.start_time != 0 )
2594 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2595 }
2596
2597 tbuf2[0] = '\0';
2598 if ( tt.length > 0.0 ) {
2599 gdouble len_in_units;
2600
2601 // Setup info dependent on distance units
2602 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2603 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2604 len_in_units = VIK_METERS_TO_MILES(tt.length);
2605 }
2606 else {
2607 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2608 len_in_units = tt.length/1000.0;
2609 }
2610
2611 // Timing information if available
2612 tbuf1[0] = '\0';
2613 if ( tt.duration > 0 ) {
2614 g_snprintf (tbuf1, sizeof(tbuf1),
2615 _(" in %d:%02d hrs:mins"),
2616 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2617 }
2618 g_snprintf (tbuf2, sizeof(tbuf2),
2619 _("\n%sTotal Length %.1f %s%s"),
2620 tbuf3, len_in_units, tbuf4, tbuf1);
2621 }
2622
2623 // Put together all the elements to form compact tooltip text
2624 g_snprintf (tmp_buf, sizeof(tmp_buf),
2625 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2626 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2627
2628 g_date_free (gdate_start);
2629 g_date_free (gdate_end);
2630
2631 }
2632
2633 return tmp_buf;
2634}
2635
2636static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2637{
2638 switch ( subtype )
2639 {
2640 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2641 {
2642 // Very simple tooltip - may expand detail in the future...
2643 static gchar tmp_buf[32];
2644 g_snprintf (tmp_buf, sizeof(tmp_buf),
2645 _("Tracks: %d"),
2646 g_hash_table_size (l->tracks));
2647 return tmp_buf;
2648 }
2649 break;
2650 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2651 {
2652 // Very simple tooltip - may expand detail in the future...
2653 static gchar tmp_buf[32];
2654 g_snprintf (tmp_buf, sizeof(tmp_buf),
2655 _("Routes: %d"),
2656 g_hash_table_size (l->routes));
2657 return tmp_buf;
2658 }
2659 break;
2660
2661 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2662 // Same tooltip for a route
2663 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2664 {
2665 VikTrack *tr;
2666 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2667 tr = g_hash_table_lookup ( l->tracks, sublayer );
2668 else
2669 tr = g_hash_table_lookup ( l->routes, sublayer );
2670
2671 if ( tr ) {
2672 // Could be a better way of handling strings - but this works...
2673 gchar time_buf1[20];
2674 gchar time_buf2[20];
2675 time_buf1[0] = '\0';
2676 time_buf2[0] = '\0';
2677 static gchar tmp_buf[100];
2678 // Compact info: Short date eg (11/20/99), duration and length
2679 // Hopefully these are the things that are most useful and so promoted into the tooltip
2680 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2681 // %x The preferred date representation for the current locale without the time.
2682 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2683 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2684 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2685 if ( dur > 0 )
2686 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2687 }
2688 }
2689 // Get length and consider the appropriate distance units
2690 gdouble tr_len = vik_track_get_length(tr);
2691 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2692 switch (dist_units) {
2693 case VIK_UNITS_DISTANCE_KILOMETRES:
2694 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2695 break;
2696 case VIK_UNITS_DISTANCE_MILES:
2697 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2698 break;
2699 default:
2700 break;
2701 }
2702 return tmp_buf;
2703 }
2704 }
2705 break;
2706 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2707 {
2708 // Very simple tooltip - may expand detail in the future...
2709 static gchar tmp_buf[32];
2710 g_snprintf (tmp_buf, sizeof(tmp_buf),
2711 _("Waypoints: %d"),
2712 g_hash_table_size (l->waypoints));
2713 return tmp_buf;
2714 }
2715 break;
2716 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2717 {
2718 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2719 // NB It's OK to return NULL
2720 if ( w ) {
2721 if ( w->comment )
2722 return w->comment;
2723 else
2724 return w->description;
2725 }
2726 }
2727 break;
2728 default: break;
2729 }
2730 return NULL;
2731}
2732
2733#define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2734
2735/**
2736 * set_statusbar_msg_info_trkpt:
2737 *
2738 * Function to show track point information on the statusbar
2739 * Items displayed is controlled by the settings format code
2740 */
2741static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2742{
2743 gchar *statusbar_format_code = NULL;
2744 gboolean need2free = FALSE;
2745 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2746 // Otherwise use default
2747 statusbar_format_code = g_strdup ( "KEATDN" );
2748 need2free = TRUE;
2749 }
2750
2751 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2752 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2753 g_free ( msg );
2754
2755 if ( need2free )
2756 g_free ( statusbar_format_code );
2757}
2758
2759/*
2760 * Function to show basic waypoint information on the statusbar
2761 */
2762static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2763{
2764 gchar tmp_buf1[64];
2765 switch (a_vik_get_units_height ()) {
2766 case VIK_UNITS_HEIGHT_FEET:
2767 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2768 break;
2769 default:
2770 //VIK_UNITS_HEIGHT_METRES:
2771 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2772 }
2773
2774 // Position part
2775 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2776 // one can easily use the current pointer position to see this if needed
2777 gchar *lat = NULL, *lon = NULL;
2778 static struct LatLon ll;
2779 vik_coord_to_latlon (&(wpt->coord), &ll);
2780 a_coords_latlon_to_string ( &ll, &lat, &lon );
2781
2782 // Combine parts to make overall message
2783 gchar *msg;
2784 if ( wpt->comment )
2785 // Add comment if available
2786 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2787 else
2788 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2789 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2790 g_free ( lat );
2791 g_free ( lon );
2792 g_free ( msg );
2793}
2794
2795/**
2796 * General layer selection function, find out which bit is selected and take appropriate action
2797 */
2798static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2799{
2800 // Reset
2801 l->current_wp = NULL;
2802 l->current_wp_id = NULL;
2803 trw_layer_cancel_current_tp ( l, FALSE );
2804
2805 // Clear statusbar
2806 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2807
2808 switch ( type )
2809 {
2810 case VIK_TREEVIEW_TYPE_LAYER:
2811 {
2812 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2813 /* Mark for redraw */
2814 return TRUE;
2815 }
2816 break;
2817
2818 case VIK_TREEVIEW_TYPE_SUBLAYER:
2819 {
2820 switch ( subtype )
2821 {
2822 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2823 {
2824 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2825 /* Mark for redraw */
2826 return TRUE;
2827 }
2828 break;
2829 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2830 {
2831 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2832 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2833 /* Mark for redraw */
2834 return TRUE;
2835 }
2836 break;
2837 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2838 {
2839 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2840 /* Mark for redraw */
2841 return TRUE;
2842 }
2843 break;
2844 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2845 {
2846 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2847 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2848 /* Mark for redraw */
2849 return TRUE;
2850 }
2851 break;
2852 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2853 {
2854 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2855 /* Mark for redraw */
2856 return TRUE;
2857 }
2858 break;
2859 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2860 {
2861 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2862 if ( wpt ) {
2863 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2864 // Show some waypoint info
2865 set_statusbar_msg_info_wpt ( l, wpt );
2866 /* Mark for redraw */
2867 return TRUE;
2868 }
2869 }
2870 break;
2871 default:
2872 {
2873 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2874 }
2875 break;
2876 }
2877 return FALSE;
2878 }
2879 break;
2880
2881 default:
2882 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2883 break;
2884 }
2885}
2886
2887GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2888{
2889 return l->tracks;
2890}
2891
2892GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2893{
2894 return l->routes;
2895}
2896
2897GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2898{
2899 return l->waypoints;
2900}
2901
2902GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
2903{
2904 return vtl->tracks_iters;
2905}
2906
2907GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
2908{
2909 return vtl->routes_iters;
2910}
2911
2912GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
2913{
2914 return vtl->waypoints;
2915}
2916
2917gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2918{
2919 return ! ( g_hash_table_size ( vtl->tracks ) ||
2920 g_hash_table_size ( vtl->routes ) ||
2921 g_hash_table_size ( vtl->waypoints ) );
2922}
2923
2924gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2925{
2926 return vtl->tracks_visible;
2927}
2928
2929gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2930{
2931 return vtl->routes_visible;
2932}
2933
2934gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2935{
2936 return vtl->waypoints_visible;
2937}
2938
2939/*
2940 * ATM use a case sensitive find
2941 * Finds the first one
2942 */
2943static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2944{
2945 if ( wp && wp->name )
2946 if ( ! strcmp ( wp->name, name ) )
2947 return TRUE;
2948 return FALSE;
2949}
2950
2951/*
2952 * Get waypoint by name - not guaranteed to be unique
2953 * Finds the first one
2954 */
2955VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2956{
2957 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2958}
2959
2960/*
2961 * ATM use a case sensitive find
2962 * Finds the first one
2963 */
2964static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2965{
2966 if ( trk && trk->name )
2967 if ( ! strcmp ( trk->name, name ) )
2968 return TRUE;
2969 return FALSE;
2970}
2971
2972/*
2973 * Get track by name - not guaranteed to be unique
2974 * Finds the first one
2975 */
2976VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2977{
2978 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2979}
2980
2981/*
2982 * Get route by name - not guaranteed to be unique
2983 * Finds the first one
2984 */
2985VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2986{
2987 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2988}
2989
2990static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2991{
2992 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2993 maxmin[0].lat = trk->bbox.north;
2994 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2995 maxmin[1].lat = trk->bbox.south;
2996 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2997 maxmin[0].lon = trk->bbox.east;
2998 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2999 maxmin[1].lon = trk->bbox.west;
3000}
3001
3002static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3003{
3004 // Continually reuse maxmin to find the latest maximum and minimum values
3005 // First set to waypoints bounds
3006 maxmin[0].lat = vtl->waypoints_bbox.north;
3007 maxmin[1].lat = vtl->waypoints_bbox.south;
3008 maxmin[0].lon = vtl->waypoints_bbox.east;
3009 maxmin[1].lon = vtl->waypoints_bbox.west;
3010 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3011 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3012}
3013
3014gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3015{
3016 /* 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... */
3017 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3018 trw_layer_find_maxmin (vtl, maxmin);
3019 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3020 return FALSE;
3021 else
3022 {
3023 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3024 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3025 return TRUE;
3026 }
3027}
3028
3029static void trw_layer_centerize ( menu_array_layer values )
3030{
3031 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3032 VikCoord coord;
3033 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3034 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3035 else
3036 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3037}
3038
3039void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3040{
3041 /* First set the center [in case previously viewing from elsewhere] */
3042 /* Then loop through zoom levels until provided positions are in view */
3043 /* This method is not particularly fast - but should work well enough */
3044 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3045 VikCoord coord;
3046 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3047 vik_viewport_set_center_coord ( vvp, &coord );
3048
3049 /* Convert into definite 'smallest' and 'largest' positions */
3050 struct LatLon minmin;
3051 if ( maxmin[0].lat < maxmin[1].lat )
3052 minmin.lat = maxmin[0].lat;
3053 else
3054 minmin.lat = maxmin[1].lat;
3055
3056 struct LatLon maxmax;
3057 if ( maxmin[0].lon > maxmin[1].lon )
3058 maxmax.lon = maxmin[0].lon;
3059 else
3060 maxmax.lon = maxmin[1].lon;
3061
3062 /* Never zoom in too far - generally not that useful, as too close ! */
3063 /* Always recalculate the 'best' zoom level */
3064 gdouble zoom = 1.0;
3065 vik_viewport_set_zoom ( vvp, zoom );
3066
3067 gdouble min_lat, max_lat, min_lon, max_lon;
3068 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3069 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3070 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3071 /* NB I think the logic used in this test to determine if the bounds is within view
3072 fails if track goes across 180 degrees longitude.
3073 Hopefully that situation is not too common...
3074 Mind you viking doesn't really do edge locations to well anyway */
3075 if ( min_lat < minmin.lat &&
3076 max_lat > minmin.lat &&
3077 min_lon < maxmax.lon &&
3078 max_lon > maxmax.lon )
3079 /* Found within zoom level */
3080 break;
3081
3082 /* Try next */
3083 zoom = zoom * 2;
3084 vik_viewport_set_zoom ( vvp, zoom );
3085 }
3086}
3087
3088gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3089{
3090 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3091 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3092 trw_layer_find_maxmin (vtl, maxmin);
3093 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3094 return FALSE;
3095 else {
3096 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3097 return TRUE;
3098 }
3099}
3100
3101static void trw_layer_auto_view ( menu_array_layer values )
3102{
3103 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3104 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3105 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3106 vik_layers_panel_emit_update ( vlp );
3107 }
3108 else
3109 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3110}
3111
3112static void trw_layer_export ( menu_array_layer values, const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3113{
3114 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3115 GtkWidget *file_selector;
3116 const gchar *fn;
3117 gboolean failed = FALSE;
3118 file_selector = gtk_file_chooser_dialog_new (title,
3119 NULL,
3120 GTK_FILE_CHOOSER_ACTION_SAVE,
3121 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3122 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3123 NULL);
3124 gchar *cwd = g_get_current_dir();
3125 if ( cwd ) {
3126 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3127 g_free ( cwd );
3128 }
3129
3130 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3131
3132 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3133 {
3134 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3135 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE ||
3136 a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3137 {
3138 gtk_widget_hide ( file_selector );
3139 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3140 failed = ! a_file_export ( vtl, fn, file_type, trk, TRUE );
3141 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3142 break;
3143 }
3144 }
3145 gtk_widget_destroy ( file_selector );
3146 if ( failed )
3147 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("The filename you requested could not be opened for writing.") );
3148}
3149
3150static void trw_layer_export_gpspoint ( menu_array_layer values )
3151{
3152 trw_layer_export ( values, _("Export Layer"), vik_layer_get_name(VIK_LAYER(values[MA_VTL])), NULL, FILE_TYPE_GPSPOINT );
3153}
3154
3155static void trw_layer_export_gpsmapper ( menu_array_layer values )
3156{
3157 trw_layer_export ( values, _("Export Layer"), vik_layer_get_name(VIK_LAYER(values[MA_VTL])), NULL, FILE_TYPE_GPSMAPPER );
3158}
3159
3160static void trw_layer_export_gpx ( menu_array_layer values )
3161{
3162 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3163 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])) );
3164 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3165 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3166
3167 trw_layer_export ( values, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3168
3169 g_free ( auto_save_name );
3170}
3171
3172static void trw_layer_export_kml ( menu_array_layer values )
3173{
3174 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3175 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])) );
3176 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3177 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3178
3179 trw_layer_export ( values, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3180
3181 g_free ( auto_save_name );
3182}
3183
3184/**
3185 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3186 *
3187 */
3188static void trw_layer_export_external_gpx ( menu_array_layer values, const gchar* external_program )
3189{
3190 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3191 gchar *name_used = NULL;
3192 int fd;
3193
3194 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3195 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3196 gboolean failed = ! a_file_export ( vtl, name_used, FILE_TYPE_GPX, NULL, TRUE);
3197 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3198 if (failed) {
3199 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not create temporary file for export.") );
3200 }
3201 else {
3202 GError *err = NULL;
3203 gchar *quoted_file = g_shell_quote ( name_used );
3204 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3205 g_free ( quoted_file );
3206 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3207 {
3208 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s."), external_program );
3209 g_error_free ( err );
3210 }
3211 g_free ( cmd );
3212 }
3213 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3214 //g_remove ( name_used );
3215 // Perhaps should be deleted when the program ends?
3216 // For now leave it to the user to delete it / use system temp cleanup methods.
3217 g_free ( name_used );
3218 }
3219}
3220
3221static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3222{
3223 trw_layer_export_external_gpx ( values, a_vik_get_external_gpx_program_1() );
3224}
3225
3226static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3227{
3228 trw_layer_export_external_gpx ( values, a_vik_get_external_gpx_program_2() );
3229}
3230
3231static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3232{
3233 menu_array_layer data;
3234 data[MA_VTL] = values[MA_VTL];
3235 data[MA_VLP] = values[MA_VLP];
3236
3237 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3238 VikTrack *trk;
3239 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3240 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3241 else
3242 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3243
3244 if ( !trk || !trk->name )
3245 return;
3246
3247 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3248 gchar *auto_save_name = g_strdup ( trk->name );
3249 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3250 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3251
3252 gchar *label = NULL;
3253 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3254 label = _("Export Route as GPX");
3255 else
3256 label = _("Export Track as GPX");
3257 trw_layer_export ( data, label, auto_save_name, trk, FILE_TYPE_GPX );
3258
3259 g_free ( auto_save_name );
3260}
3261
3262gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3263{
3264 wpu_udata *user_data = udata;
3265 if ( wp == user_data->wp ) {
3266 user_data->uuid = id;
3267 return TRUE;
3268 }
3269 return FALSE;
3270}
3271
3272static void trw_layer_goto_wp ( menu_array_layer values )
3273{
3274 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3275 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3276 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3277 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3278 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3279 GTK_STOCK_CANCEL,
3280 GTK_RESPONSE_REJECT,
3281 GTK_STOCK_OK,
3282 GTK_RESPONSE_ACCEPT,
3283 NULL);
3284
3285 GtkWidget *label, *entry;
3286 label = gtk_label_new(_("Waypoint Name:"));
3287 entry = gtk_entry_new();
3288
3289 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3290 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3291 gtk_widget_show_all ( label );
3292 gtk_widget_show_all ( entry );
3293
3294 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3295
3296 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3297 {
3298 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3299 // Find *first* wp with the given name
3300 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3301
3302 if ( !wp )
3303 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3304 else
3305 {
3306 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord) );
3307 vik_layers_panel_emit_update ( vlp );
3308
3309 // Find and select on the side panel
3310 wpu_udata udata;
3311 udata.wp = wp;
3312 udata.uuid = NULL;
3313
3314 // Hmmm, want key of it
3315 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3316
3317 if ( wpf && udata.uuid ) {
3318 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3319 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3320 }
3321
3322 break;
3323 }
3324
3325 g_free ( name );
3326
3327 }
3328 gtk_widget_destroy ( dia );
3329}
3330
3331gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3332{
3333 gchar *default_name = highest_wp_number_get(vtl);
3334 VikWaypoint *wp = vik_waypoint_new();
3335 gchar *returned_name;
3336 gboolean updated;
3337 wp->coord = *def_coord;
3338
3339 // Attempt to auto set height if DEM data is available
3340 vik_waypoint_apply_dem_data ( wp, TRUE );
3341
3342 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3343
3344 if ( returned_name )
3345 {
3346 wp->visible = TRUE;
3347 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3348 g_free (default_name);
3349 g_free (returned_name);
3350 return TRUE;
3351 }
3352 g_free (default_name);
3353 vik_waypoint_free(wp);
3354 return FALSE;
3355}
3356
3357static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3358{
3359 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3360 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3361 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3362 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3363 VikViewport *vvp = vik_window_viewport(vw);
3364
3365 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3366 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3367 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3368 trw_layer_calculate_bounds_waypoints ( vtl );
3369 vik_layers_panel_emit_update ( vlp );
3370}
3371
3372static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3373{
3374 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3375 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3376 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3377
3378 trw_layer_find_maxmin (vtl, maxmin);
3379 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3380 trw_layer_calculate_bounds_waypoints ( vtl );
3381 vik_layers_panel_emit_update ( vlp );
3382}
3383
3384#ifdef VIK_CONFIG_GEOTAG
3385static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3386{
3387 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3388 if ( wp )
3389 // Update directly - not changing the mtime
3390 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3391}
3392
3393static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3394{
3395 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3396 if ( wp )
3397 // Update directly
3398 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3399}
3400
3401/*
3402 * Use code in separate file for this feature as reasonably complex
3403 */
3404static void trw_layer_geotagging_track ( menu_array_sublayer values )
3405{
3406 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3407 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3408 // Unset so can be reverified later if necessary
3409 vtl->has_verified_thumbnails = FALSE;
3410
3411 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3412 vtl,
3413 NULL,
3414 track );
3415}
3416
3417static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3418{
3419 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3420 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3421
3422 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3423 vtl,
3424 wpt,
3425 NULL );
3426}
3427
3428static void trw_layer_geotagging ( menu_array_layer values )
3429{
3430 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3431 // Unset so can be reverified later if necessary
3432 vtl->has_verified_thumbnails = FALSE;
3433
3434 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3435 vtl,
3436 NULL,
3437 NULL );
3438}
3439#endif
3440
3441// 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3442
3443static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3444{
3445 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3446 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3447 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3448 VikViewport *vvp = vik_window_viewport(vw);
3449
3450 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3451}
3452
3453/*
3454 * Acquire into this TRW Layer straight from GPS Device
3455 */
3456static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3457{
3458 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3459 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3460}
3461
3462/*
3463 * Acquire into this TRW Layer from Directions
3464 */
3465static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3466{
3467 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3468}
3469
3470/*
3471 * Acquire into this TRW Layer from an entered URL
3472 */
3473static void trw_layer_acquire_url_cb ( menu_array_layer values )
3474{
3475 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3476 trw_layer_acquire ( values, &vik_datasource_url_interface );
3477}
3478
3479#ifdef VIK_CONFIG_OPENSTREETMAP
3480/*
3481 * Acquire into this TRW Layer from OSM
3482 */
3483static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3484{
3485 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3486}
3487
3488/**
3489 * Acquire into this TRW Layer from OSM for 'My' Traces
3490 */
3491static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3492{
3493 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3494}
3495#endif
3496
3497#ifdef VIK_CONFIG_GEOCACHES
3498/*
3499 * Acquire into this TRW Layer from Geocaching.com
3500 */
3501static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3502{
3503 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3504}
3505#endif
3506
3507#ifdef VIK_CONFIG_GEOTAG
3508/*
3509 * Acquire into this TRW Layer from images
3510 */
3511static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3512{
3513 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3514
3515 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3516 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3517
3518 // Reverify thumbnails as they may have changed
3519 vtl->has_verified_thumbnails = FALSE;
3520 trw_layer_verify_thumbnails ( vtl, NULL );
3521}
3522#endif
3523
3524static void trw_layer_gps_upload ( menu_array_layer values )
3525{
3526 menu_array_sublayer data;
3527 gint ii;
3528 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3529 data[ii] = NULL;
3530 data[MA_VTL] = values[MA_VTL];
3531 data[MA_VLP] = values[MA_VLP];
3532
3533 trw_layer_gps_upload_any ( data );
3534}
3535
3536/**
3537 * If pass_along[3] is defined that this will upload just that track
3538 */
3539static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3540{
3541 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3542 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3543
3544 // May not actually get a track here as values[2&3] can be null
3545 VikTrack *track = NULL;
3546 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3547 gboolean xfer_all = FALSE;
3548
3549 if ( values[MA_SUBTYPE] ) {
3550 xfer_all = FALSE;
3551 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3552 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3553 xfer_type = RTE;
3554 }
3555 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3556 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3557 xfer_type = TRK;
3558 }
3559 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3560 xfer_type = WPT;
3561 }
3562 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3563 xfer_type = RTE;
3564 }
3565 }
3566 else if ( !values[MA_CONFIRM] )
3567 xfer_all = TRUE; // i.e. whole layer
3568
3569 if (track && !track->visible) {
3570 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3571 return;
3572 }
3573
3574 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3575 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3576 GTK_DIALOG_DESTROY_WITH_PARENT,
3577 GTK_STOCK_OK,
3578 GTK_RESPONSE_ACCEPT,
3579 GTK_STOCK_CANCEL,
3580 GTK_RESPONSE_REJECT,
3581 NULL );
3582
3583 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3584 GtkWidget *response_w = NULL;
3585#if GTK_CHECK_VERSION (2, 20, 0)
3586 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3587#endif
3588
3589 if ( response_w )
3590 gtk_widget_grab_focus ( response_w );
3591
3592 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3593
3594 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3595 datasource_gps_clean_up ( dgs );
3596 gtk_widget_destroy ( dialog );
3597 return;
3598 }
3599
3600 // Get info from reused datasource dialog widgets
3601 gchar* protocol = datasource_gps_get_protocol ( dgs );
3602 gchar* port = datasource_gps_get_descriptor ( dgs );
3603 // NB don't free the above strings as they're references to values held elsewhere
3604 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3605 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3606 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3607 gboolean turn_off = datasource_gps_get_off ( dgs );
3608
3609 gtk_widget_destroy ( dialog );
3610
3611 // When called from the viewport - work the corresponding layerspanel:
3612 if ( !vlp ) {
3613 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3614 }
3615
3616 // Apply settings to transfer to the GPS device
3617 vik_gps_comm ( vtl,
3618 track,
3619 GPS_UP,
3620 protocol,
3621 port,
3622 FALSE,
3623 vik_layers_panel_get_viewport (vlp),
3624 vlp,
3625 do_tracks,
3626 do_routes,
3627 do_waypoints,
3628 turn_off );
3629}
3630
3631/*
3632 * Acquire into this TRW Layer from any GPS Babel supported file
3633 */
3634static void trw_layer_acquire_file_cb ( menu_array_layer values )
3635{
3636 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3637 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3638 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3639 VikViewport *vvp = vik_window_viewport(vw);
3640
3641 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3642}
3643
3644static void trw_layer_new_wp ( menu_array_layer values )
3645{
3646 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3647 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3648 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3649 instead return true if you want to update. */
3650 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 ) {
3651 trw_layer_calculate_bounds_waypoints ( vtl );
3652 vik_layers_panel_emit_update ( vlp );
3653 }
3654}
3655
3656static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3657{
3658 vtl->current_track = vik_track_new();
3659 vik_track_set_defaults ( vtl->current_track );
3660 vtl->current_track->visible = TRUE;
3661 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3662 // Create track with the preferred colour from the layer properties
3663 vtl->current_track->color = vtl->track_color;
3664 else
3665 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3666 vtl->current_track->has_color = TRUE;
3667 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3668}
3669
3670static void trw_layer_new_track ( menu_array_layer values )
3671{
3672 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3673
3674 if ( ! vtl->current_track ) {
3675 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3676 new_track_create_common ( vtl, name );
3677 g_free ( name );
3678
3679 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3680 }
3681}
3682
3683static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3684{
3685 vtl->current_track = vik_track_new();
3686 vik_track_set_defaults ( vtl->current_track );
3687 vtl->current_track->visible = TRUE;
3688 vtl->current_track->is_route = TRUE;
3689 // By default make all routes red
3690 vtl->current_track->has_color = TRUE;
3691 gdk_color_parse ( "red", &vtl->current_track->color );
3692 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3693}
3694
3695static void trw_layer_new_route ( menu_array_layer values )
3696{
3697 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3698
3699 if ( ! vtl->current_track ) {
3700 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3701 new_route_create_common ( vtl, name );
3702 g_free ( name );
3703 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3704 }
3705}
3706
3707static void trw_layer_auto_routes_view ( menu_array_layer values )
3708{
3709 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3710 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3711
3712 if ( g_hash_table_size (vtl->routes) > 0 ) {
3713 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3714 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3715 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3716 vik_layers_panel_emit_update ( vlp );
3717 }
3718}
3719
3720
3721static void trw_layer_finish_track ( menu_array_layer values )
3722{
3723 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3724 vtl->current_track = NULL;
3725 vik_layer_emit_update ( VIK_LAYER(vtl) );
3726}
3727
3728static void trw_layer_auto_tracks_view ( menu_array_layer values )
3729{
3730 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3731 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3732
3733 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3734 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3735 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3736 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3737 vik_layers_panel_emit_update ( vlp );
3738 }
3739}
3740
3741static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3742{
3743 /* NB do not care if wp is visible or not */
3744 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3745}
3746
3747static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3748{
3749 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3750 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3751
3752 /* Only 1 waypoint - jump straight to it */
3753 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3754 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3755 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3756 }
3757 /* If at least 2 waypoints - find center and then zoom to fit */
3758 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3759 {
3760 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3761 maxmin[0].lat = vtl->waypoints_bbox.north;
3762 maxmin[1].lat = vtl->waypoints_bbox.south;
3763 maxmin[0].lon = vtl->waypoints_bbox.east;
3764 maxmin[1].lon = vtl->waypoints_bbox.west;
3765 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3766 }
3767
3768 vik_layers_panel_emit_update ( vlp );
3769}
3770
3771void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3772{
3773 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3774}
3775
3776void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3777{
3778 if ( values[MA_MISC] ) {
3779 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3780 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3781 }
3782}
3783
3784static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3785{
3786 static menu_array_layer pass_along;
3787 GtkWidget *item;
3788 GtkWidget *export_submenu;
3789 pass_along[MA_VTL] = vtl;
3790 pass_along[MA_VLP] = vlp;
3791
3792 item = gtk_menu_item_new();
3793 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3794 gtk_widget_show ( item );
3795
3796 if ( vtl->current_track ) {
3797 if ( vtl->current_track->is_route )
3798 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3799 else
3800 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3801 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3802 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3803 gtk_widget_show ( item );
3804
3805 // Add separator
3806 item = gtk_menu_item_new ();
3807 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3808 gtk_widget_show ( item );
3809 }
3810
3811 /* Now with icons */
3812 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3815 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3816 gtk_widget_show ( item );
3817
3818 GtkWidget *view_submenu = gtk_menu_new();
3819 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3820 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3821 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3822 gtk_widget_show ( item );
3823 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3824
3825 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3827 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3828 gtk_widget_show ( item );
3829
3830 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3832 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3833 gtk_widget_show ( item );
3834
3835 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3836 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3837 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3838 gtk_widget_show ( item );
3839
3840 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3841 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3842 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3843 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3844 gtk_widget_show ( item );
3845
3846 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3848 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3849 gtk_widget_show ( item );
3850
3851 export_submenu = gtk_menu_new ();
3852 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3853 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3854 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3855 gtk_widget_show ( item );
3856 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3857
3858 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3859 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3860 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3861 gtk_widget_show ( item );
3862
3863 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3864 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3865 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3866 gtk_widget_show ( item );
3867
3868 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3869 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3870 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3871 gtk_widget_show ( item );
3872
3873 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3874 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3875 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3876 gtk_widget_show ( item );
3877
3878 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
3879 item = gtk_menu_item_new_with_mnemonic ( external1 );
3880 g_free ( external1 );
3881 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3882 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3883 gtk_widget_show ( item );
3884
3885 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
3886 item = gtk_menu_item_new_with_mnemonic ( external2 );
3887 g_free ( external2 );
3888 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3889 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3890 gtk_widget_show ( item );
3891
3892 GtkWidget *new_submenu = gtk_menu_new();
3893 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3895 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3896 gtk_widget_show(item);
3897 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3898
3899 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3902 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3903 gtk_widget_show ( item );
3904
3905 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3906 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3908 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3909 gtk_widget_show ( item );
3910 // Make it available only when a new track *not* already in progress
3911 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3912
3913 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3915 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3916 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3917 gtk_widget_show ( item );
3918 // Make it available only when a new track *not* already in progress
3919 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3920
3921#ifdef VIK_CONFIG_GEOTAG
3922 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3924 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3925 gtk_widget_show ( item );
3926#endif
3927
3928 GtkWidget *acquire_submenu = gtk_menu_new ();
3929 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3930 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3931 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3932 gtk_widget_show ( item );
3933 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3934
3935 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3937 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3938 gtk_widget_show ( item );
3939
3940 /* FIXME: only add menu when at least a routing engine has support for Directions */
3941 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3942 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3943 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3944 gtk_widget_show ( item );
3945
3946#ifdef VIK_CONFIG_OPENSTREETMAP
3947 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3948 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3949 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3950 gtk_widget_show ( item );
3951
3952 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3954 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3955 gtk_widget_show ( item );
3956#endif
3957
3958 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
3959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
3960 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3961 gtk_widget_show ( item );
3962
3963#ifdef VIK_CONFIG_GEONAMES
3964 GtkWidget *wikipedia_submenu = gtk_menu_new();
3965 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3966 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3967 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3968 gtk_widget_show(item);
3969 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3970
3971 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3972 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3974 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3975 gtk_widget_show ( item );
3976
3977 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3980 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3981 gtk_widget_show ( item );
3982#endif
3983
3984#ifdef VIK_CONFIG_GEOCACHES
3985 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3986 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3987 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3988 gtk_widget_show ( item );
3989#endif
3990
3991#ifdef VIK_CONFIG_GEOTAG
3992 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3994 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3995 gtk_widget_show ( item );
3996#endif
3997
3998 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4000 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4001 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4002 gtk_widget_show ( item );
4003
4004 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4005
4006 GtkWidget *upload_submenu = gtk_menu_new ();
4007 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4009 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4010 gtk_widget_show ( item );
4011 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4012
4013 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4014 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4016 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4017 gtk_widget_show ( item );
4018
4019#ifdef VIK_CONFIG_OPENSTREETMAP
4020 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4021 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4023 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4024 gtk_widget_show ( item );
4025#endif
4026
4027 GtkWidget *delete_submenu = gtk_menu_new ();
4028 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4029 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4030 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4031 gtk_widget_show ( item );
4032 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4033
4034 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4035 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4037 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4038 gtk_widget_show ( item );
4039
4040 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4043 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4044 gtk_widget_show ( item );
4045
4046 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4047 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4049 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4050 gtk_widget_show ( item );
4051
4052 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4053 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4055 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4056 gtk_widget_show ( item );
4057
4058 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4059 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4061 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4062 gtk_widget_show ( item );
4063
4064 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4065 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4067 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4068 gtk_widget_show ( item );
4069
4070 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4071 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4072 if ( item ) {
4073 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4074 gtk_widget_show ( item );
4075 }
4076
4077 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4078 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4079 if ( item ) {
4080 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4081 gtk_widget_show ( item );
4082 }
4083
4084 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4087 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4088 gtk_widget_show ( item );
4089 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4090
4091 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4092 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4094 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4095 gtk_widget_show ( item );
4096 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4097}
4098
4099// Fake Waypoint UUIDs vi simple increasing integer
4100static guint wp_uuid = 0;
4101
4102void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4103{
4104 wp_uuid++;
4105
4106 vik_waypoint_set_name (wp, name);
4107
4108 if ( VIK_LAYER(vtl)->realized )
4109 {
4110 // Do we need to create the sublayer:
4111 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4112 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4113 }
4114
4115 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4116
4117 // Visibility column always needed for waypoints
4118 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
4119
4120 // Actual setting of visibility dependent on the waypoint
4121 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4122
4123 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4124
4125 // Sort now as post_read is not called on a realized waypoint
4126 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4127 }
4128
4129 highest_wp_number_add_wp(vtl, name);
4130 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4131
4132}
4133
4134// Fake Track UUIDs vi simple increasing integer
4135static guint tr_uuid = 0;
4136
4137void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4138{
4139 tr_uuid++;
4140
4141 vik_track_set_name (t, name);
4142
4143 if ( VIK_LAYER(vtl)->realized )
4144 {
4145 // Do we need to create the sublayer:
4146 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4147 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4148 }
4149
4150 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4151 // Visibility column always needed for tracks
4152 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 );
4153
4154 // Actual setting of visibility dependent on the track
4155 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4156
4157 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4158
4159 // Sort now as post_read is not called on a realized track
4160 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4161 }
4162
4163 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4164
4165 trw_layer_update_treeview ( vtl, t );
4166}
4167
4168// Fake Route UUIDs vi simple increasing integer
4169static guint rt_uuid = 0;
4170
4171void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4172{
4173 rt_uuid++;
4174
4175 vik_track_set_name (t, name);
4176
4177 if ( VIK_LAYER(vtl)->realized )
4178 {
4179 // Do we need to create the sublayer:
4180 if ( g_hash_table_size (vtl->routes) == 0 ) {
4181 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4182 }
4183
4184 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4185 // Visibility column always needed for routes
4186 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
4187 // Actual setting of visibility dependent on the route
4188 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4189
4190 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4191
4192 // Sort now as post_read is not called on a realized route
4193 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4194 }
4195
4196 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4197
4198 trw_layer_update_treeview ( vtl, t );
4199}
4200
4201/* to be called whenever a track has been deleted or may have been changed. */
4202void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4203{
4204 if (vtl->current_tp_track == trk )
4205 trw_layer_cancel_current_tp ( vtl, FALSE );
4206}
4207
4208/**
4209 * Normally this is done to due the waypoint size preference having changed
4210 */
4211void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4212{
4213 GHashTableIter iter;
4214 gpointer key, value;
4215
4216 // Foreach waypoint
4217 g_hash_table_iter_init ( &iter, vtl->waypoints );
4218 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4219 VikWaypoint *wp = VIK_WAYPOINT(value);
4220 if ( wp->symbol ) {
4221 // Reapply symbol setting to update the pixbuf
4222 gchar *tmp_symbol = g_strdup ( wp->symbol );
4223 vik_waypoint_set_symbol ( wp, tmp_symbol );
4224 g_free ( tmp_symbol );
4225 }
4226 }
4227}
4228
4229/**
4230 * trw_layer_new_unique_sublayer_name:
4231 *
4232 * Allocates a unique new name
4233 */
4234gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4235{
4236 gint i = 2;
4237 gchar *newname = g_strdup(name);
4238
4239 gpointer id = NULL;
4240 do {
4241 id = NULL;
4242 switch ( sublayer_type ) {
4243 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4244 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4245 break;
4246 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4247 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4248 break;
4249 default:
4250 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4251 break;
4252 }
4253 // If found a name already in use try adding 1 to it and we try again
4254 if ( id ) {
4255 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4256 g_free(newname);
4257 newname = new_newname;
4258 i++;
4259 }
4260 } while ( id != NULL);
4261
4262 return newname;
4263}
4264
4265void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4266{
4267 // No more uniqueness of name forced when loading from a file
4268 // This now makes this function a little redunant as we just flow the parameters through
4269 vik_trw_layer_add_waypoint ( vtl, name, wp );
4270}
4271
4272void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4273{
4274 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4275 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4276 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4277 vik_track_free ( tr );
4278 vtl->route_finder_append = FALSE; /* this means we have added it */
4279 } else {
4280
4281 // No more uniqueness of name forced when loading from a file
4282 if ( tr->is_route )
4283 vik_trw_layer_add_route ( vtl, name, tr );
4284 else
4285 vik_trw_layer_add_track ( vtl, name, tr );
4286
4287 if ( vtl->route_finder_check_added_track ) {
4288 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4289 vtl->route_finder_added_track = tr;
4290 }
4291 }
4292}
4293
4294static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4295{
4296 *l = g_list_append(*l, id);
4297}
4298
4299/*
4300 * Move an item from one TRW layer to another TRW layer
4301 */
4302static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4303{
4304 // TODO reconsider strategy when moving within layer (if anything...)
4305 gboolean rename = ( vtl_src != vtl_dest );
4306 if ( ! rename )
4307 return;
4308
4309 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4310 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4311
4312 gchar *newname;
4313 if ( rename )
4314 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4315 else
4316 newname = g_strdup ( trk->name );
4317
4318 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4319 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4320 g_free ( newname );
4321 vik_trw_layer_delete_track ( vtl_src, trk );
4322 }
4323
4324 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4325 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4326
4327 gchar *newname;
4328 if ( rename )
4329 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4330 else
4331 newname = g_strdup ( trk->name );
4332
4333 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4334 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4335 g_free ( newname );
4336 vik_trw_layer_delete_route ( vtl_src, trk );
4337 }
4338
4339 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4340 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4341
4342 gchar *newname;
4343 if ( rename )
4344 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4345 else
4346 newname = g_strdup ( wp->name );
4347
4348 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4349 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4350 g_free ( newname );
4351 trw_layer_delete_waypoint ( vtl_src, wp );
4352
4353 // Recalculate bounds even if not renamed as maybe dragged between layers
4354 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4355 trw_layer_calculate_bounds_waypoints ( vtl_src );
4356 }
4357}
4358
4359static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4360{
4361 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4362 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4363
4364 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4365 GList *items = NULL;
4366 GList *iter;
4367
4368 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4369 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4370 }
4371 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4372 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4373 }
4374 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4375 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4376 }
4377
4378 iter = items;
4379 while (iter) {
4380 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4381 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4382 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4383 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4384 } else {
4385 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4386 }
4387 iter = iter->next;
4388 }
4389 if (items)
4390 g_list_free(items);
4391 } else {
4392 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4393 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4394 }
4395}
4396
4397gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4398{
4399 trku_udata *user_data = udata;
4400 if ( trk == user_data->trk ) {
4401 user_data->uuid = id;
4402 return TRUE;
4403 }
4404 return FALSE;
4405}
4406
4407gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4408{
4409 gboolean was_visible = FALSE;
4410
4411 if ( trk && trk->name ) {
4412
4413 if ( trk == vtl->current_track ) {
4414 vtl->current_track = NULL;
4415 vtl->current_tp_track = NULL;
4416 vtl->current_tp_id = NULL;
4417 vtl->moving_tp = FALSE;
4418 }
4419
4420 was_visible = trk->visible;
4421
4422 if ( trk == vtl->route_finder_current_track )
4423 vtl->route_finder_current_track = NULL;
4424
4425 if ( trk == vtl->route_finder_added_track )
4426 vtl->route_finder_added_track = NULL;
4427
4428 trku_udata udata;
4429 udata.trk = trk;
4430 udata.uuid = NULL;
4431
4432 // Hmmm, want key of it
4433 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4434
4435 if ( trkf && udata.uuid ) {
4436 /* could be current_tp, so we have to check */
4437 trw_layer_cancel_tps_of_track ( vtl, trk );
4438
4439 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4440
4441 if ( it ) {
4442 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4443 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4444 g_hash_table_remove ( vtl->tracks, udata.uuid );
4445
4446 // If last sublayer, then remove sublayer container
4447 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4448 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4449 }
4450 }
4451 }
4452 }
4453 return was_visible;
4454}
4455
4456gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4457{
4458 gboolean was_visible = FALSE;
4459
4460 if ( trk && trk->name ) {
4461
4462 if ( trk == vtl->current_track ) {
4463 vtl->current_track = NULL;
4464 vtl->current_tp_track = NULL;
4465 vtl->current_tp_id = NULL;
4466 vtl->moving_tp = FALSE;
4467 }
4468
4469 was_visible = trk->visible;
4470
4471 if ( trk == vtl->route_finder_current_track )
4472 vtl->route_finder_current_track = NULL;
4473
4474 if ( trk == vtl->route_finder_added_track )
4475 vtl->route_finder_added_track = NULL;
4476
4477 trku_udata udata;
4478 udata.trk = trk;
4479 udata.uuid = NULL;
4480
4481 // Hmmm, want key of it
4482 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4483
4484 if ( trkf && udata.uuid ) {
4485 /* could be current_tp, so we have to check */
4486 trw_layer_cancel_tps_of_track ( vtl, trk );
4487
4488 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4489
4490 if ( it ) {
4491 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4492 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4493 g_hash_table_remove ( vtl->routes, udata.uuid );
4494
4495 // If last sublayer, then remove sublayer container
4496 if ( g_hash_table_size (vtl->routes) == 0 ) {
4497 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4498 }
4499 }
4500 }
4501 }
4502 return was_visible;
4503}
4504
4505static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4506{
4507 gboolean was_visible = FALSE;
4508
4509 if ( wp && wp->name ) {
4510
4511 if ( wp == vtl->current_wp ) {
4512 vtl->current_wp = NULL;
4513 vtl->current_wp_id = NULL;
4514 vtl->moving_wp = FALSE;
4515 }
4516
4517 was_visible = wp->visible;
4518
4519 wpu_udata udata;
4520 udata.wp = wp;
4521 udata.uuid = NULL;
4522
4523 // Hmmm, want key of it
4524 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4525
4526 if ( wpf && udata.uuid ) {
4527 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4528
4529 if ( it ) {
4530 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4531 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4532
4533 highest_wp_number_remove_wp(vtl, wp->name);
4534 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4535
4536 // If last sublayer, then remove sublayer container
4537 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4538 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4539 }
4540 }
4541 }
4542
4543 }
4544
4545 return was_visible;
4546}
4547
4548// Only for temporary use by trw_layer_delete_waypoint_by_name
4549static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4550{
4551 wpu_udata *user_data = udata;
4552 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4553 user_data->uuid = id;
4554 return TRUE;
4555 }
4556 return FALSE;
4557}
4558
4559/*
4560 * Delete a waypoint by the given name
4561 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4562 * as there be multiple waypoints with the same name
4563 */
4564static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4565{
4566 wpu_udata udata;
4567 // Fake a waypoint with the given name
4568 udata.wp = vik_waypoint_new ();
4569 vik_waypoint_set_name (udata.wp, name);
4570 // Currently only the name is used in this waypoint find function
4571 udata.uuid = NULL;
4572
4573 // Hmmm, want key of it
4574 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4575
4576 vik_waypoint_free (udata.wp);
4577
4578 if ( wpf && udata.uuid )
4579 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4580 else
4581 return FALSE;
4582}
4583
4584typedef struct {
4585 VikTrack *trk; // input
4586 gpointer uuid; // output
4587} tpu_udata;
4588
4589// Only for temporary use by trw_layer_delete_track_by_name
4590static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4591{
4592 tpu_udata *user_data = udata;
4593 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4594 user_data->uuid = id;
4595 return TRUE;
4596 }
4597 return FALSE;
4598}
4599
4600/*
4601 * Delete a track by the given name
4602 * NOTE: ATM this will delete the first encountered Track with the specified name
4603 * as there may be multiple tracks with the same name within the specified hash table
4604 */
4605static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4606{
4607 tpu_udata udata;
4608 // Fake a track with the given name
4609 udata.trk = vik_track_new ();
4610 vik_track_set_name (udata.trk, name);
4611 // Currently only the name is used in this waypoint find function
4612 udata.uuid = NULL;
4613
4614 // Hmmm, want key of it
4615 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4616
4617 vik_track_free (udata.trk);
4618
4619 if ( trkf && udata.uuid ) {
4620 // This could be a little better written...
4621 if ( vtl->tracks == ht_tracks )
4622 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4623 if ( vtl->routes == ht_tracks )
4624 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4625 return FALSE;
4626 }
4627 else
4628 return FALSE;
4629}
4630
4631static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4632{
4633 vik_treeview_item_delete (vt, it );
4634}
4635
4636void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4637{
4638
4639 vtl->current_track = NULL;
4640 vtl->route_finder_current_track = NULL;
4641 vtl->route_finder_added_track = NULL;
4642 if (vtl->current_tp_track)
4643 trw_layer_cancel_current_tp(vtl, FALSE);
4644
4645 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4646 g_hash_table_remove_all(vtl->routes_iters);
4647 g_hash_table_remove_all(vtl->routes);
4648
4649 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4650
4651 vik_layer_emit_update ( VIK_LAYER(vtl) );
4652}
4653
4654void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4655{
4656
4657 vtl->current_track = NULL;
4658 vtl->route_finder_current_track = NULL;
4659 vtl->route_finder_added_track = NULL;
4660 if (vtl->current_tp_track)
4661 trw_layer_cancel_current_tp(vtl, FALSE);
4662
4663 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4664 g_hash_table_remove_all(vtl->tracks_iters);
4665 g_hash_table_remove_all(vtl->tracks);
4666
4667 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4668
4669 vik_layer_emit_update ( VIK_LAYER(vtl) );
4670}
4671
4672void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4673{
4674 vtl->current_wp = NULL;
4675 vtl->current_wp_id = NULL;
4676 vtl->moving_wp = FALSE;
4677
4678 highest_wp_number_reset(vtl);
4679
4680 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4681 g_hash_table_remove_all(vtl->waypoints_iters);
4682 g_hash_table_remove_all(vtl->waypoints);
4683
4684 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4685
4686 vik_layer_emit_update ( VIK_LAYER(vtl) );
4687}
4688
4689static void trw_layer_delete_all_tracks ( menu_array_layer values )
4690{
4691 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4692 // Get confirmation from the user
4693 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4694 _("Are you sure you want to delete all tracks in %s?"),
4695 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4696 vik_trw_layer_delete_all_tracks (vtl);
4697}
4698
4699static void trw_layer_delete_all_routes ( menu_array_layer values )
4700{
4701 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4702 // Get confirmation from the user
4703 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4704 _("Are you sure you want to delete all routes in %s?"),
4705 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4706 vik_trw_layer_delete_all_routes (vtl);
4707}
4708
4709static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4710{
4711 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4712 // Get confirmation from the user
4713 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4714 _("Are you sure you want to delete all waypoints in %s?"),
4715 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4716 vik_trw_layer_delete_all_waypoints (vtl);
4717}
4718
4719static void trw_layer_delete_item ( menu_array_sublayer values )
4720{
4721 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4722 gboolean was_visible = FALSE;
4723 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4724 {
4725 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4726 if ( wp && wp->name ) {
4727 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4728 // Get confirmation from the user
4729 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4730 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4731 _("Are you sure you want to delete the waypoint \"%s\"?"),
4732 wp->name ) )
4733 return;
4734 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4735 }
4736 }
4737 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4738 {
4739 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4740 if ( trk && trk->name ) {
4741 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4742 // Get confirmation from the user
4743 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4744 _("Are you sure you want to delete the track \"%s\"?"),
4745 trk->name ) )
4746 return;
4747 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4748 }
4749 }
4750 else
4751 {
4752 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4753 if ( trk && trk->name ) {
4754 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4755 // Get confirmation from the user
4756 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4757 _("Are you sure you want to delete the route \"%s\"?"),
4758 trk->name ) )
4759 return;
4760 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4761 }
4762 }
4763 if ( was_visible )
4764 vik_layer_emit_update ( VIK_LAYER(vtl) );
4765}
4766
4767/**
4768 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4769 */
4770void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4771{
4772 vik_waypoint_set_name ( wp, new_name );
4773
4774 // Now update the treeview as well
4775 wpu_udata udataU;
4776 udataU.wp = wp;
4777 udataU.uuid = NULL;
4778
4779 // Need key of it for treeview update
4780 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4781
4782 if ( wpf && udataU.uuid ) {
4783 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4784
4785 if ( it ) {
4786 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4787 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4788 }
4789 }
4790}
4791
4792/**
4793 * Maintain icon of waypoint in the treeview
4794 */
4795void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4796{
4797 // update the treeview
4798 wpu_udata udataU;
4799 udataU.wp = wp;
4800 udataU.uuid = NULL;
4801
4802 // Need key of it for treeview update
4803 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4804
4805 if ( wpf && udataU.uuid ) {
4806 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4807
4808 if ( it ) {
4809 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4810 }
4811 }
4812}
4813
4814static void trw_layer_properties_item ( menu_array_sublayer values )
4815{
4816 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4817 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4818 {
4819 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4820
4821 if ( wp && wp->name )
4822 {
4823 gboolean updated = FALSE;
4824 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4825 if ( new_name )
4826 trw_layer_waypoint_rename ( vtl, wp, new_name );
4827
4828 if ( updated && values[MA_TV_ITER] )
4829 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4830
4831 if ( updated && VIK_LAYER(vtl)->visible )
4832 vik_layer_emit_update ( VIK_LAYER(vtl) );
4833 }
4834 }
4835 else
4836 {
4837 VikTrack *tr;
4838 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4839 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4840 else
4841 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4842
4843 if ( tr && tr->name )
4844 {
4845 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4846 vtl,
4847 tr,
4848 values[MA_VLP],
4849 values[MA_VVP],
4850 FALSE );
4851 }
4852 }
4853}
4854
4855/**
4856 * trw_layer_track_statistics:
4857 *
4858 * Show track statistics.
4859 * ATM jump to the stats page in the properties
4860 * TODO: consider separating the stats into an individual dialog?
4861 */
4862static void trw_layer_track_statistics ( menu_array_sublayer values )
4863{
4864 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4865 VikTrack *trk;
4866 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4867 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4868 else
4869 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4870
4871 if ( trk && trk->name ) {
4872 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4873 vtl,
4874 trk,
4875 values[MA_VLP],
4876 values[MA_VVP],
4877 TRUE );
4878 }
4879}
4880
4881/*
4882 * Update the treeview of the track id - primarily to update the icon
4883 */
4884void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4885{
4886 trku_udata udata;
4887 udata.trk = trk;
4888 udata.uuid = NULL;
4889
4890 gpointer *trkf = NULL;
4891 if ( trk->is_route )
4892 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4893 else
4894 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4895
4896 if ( trkf && udata.uuid ) {
4897
4898 GtkTreeIter *iter = NULL;
4899 if ( trk->is_route )
4900 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4901 else
4902 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4903
4904 if ( iter ) {
4905 // TODO: Make this a function
4906 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4907 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4908 ((trk->color.green & 0xff00) << 8) |
4909 (trk->color.blue & 0xff00);
4910 gdk_pixbuf_fill ( pixbuf, pixel );
4911 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4912 g_object_unref (pixbuf);
4913 }
4914
4915 }
4916}
4917
4918/*
4919 Parameter 1 -> VikLayersPanel
4920 Parameter 2 -> VikLayer
4921 Parameter 3 -> VikViewport
4922*/
4923static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4924{
4925 if ( vlp ) {
4926 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4927 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4928 }
4929 else {
4930 /* since vlp not set, vl & vvp should be valid instead! */
4931 if ( vl && vvp ) {
4932 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4933 vik_layer_emit_update ( VIK_LAYER(vl) );
4934 }
4935 }
4936}
4937
4938static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
4939{
4940 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4941 VikTrack *track;
4942 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4943 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4944 else
4945 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4946
4947 if ( track && track->trackpoints )
4948 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
4949}
4950
4951static void trw_layer_goto_track_center ( menu_array_sublayer values )
4952{
4953 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4954 VikTrack *track;
4955 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4956 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4957 else
4958 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4959
4960 if ( track && track->trackpoints )
4961 {
4962 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4963 VikCoord coord;
4964 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4965 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4966 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4967 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4968 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
4969 }
4970}
4971
4972static void trw_layer_convert_track_route ( menu_array_sublayer values )
4973{
4974 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4975 VikTrack *trk;
4976 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4977 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4978 else
4979 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4980
4981 if ( !trk )
4982 return;
4983
4984 // Converting a track to a route can be a bit more complicated,
4985 // so give a chance to change our minds:
4986 if ( !trk->is_route &&
4987 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4988 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4989
4990 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4991 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4992 return;
4993}
4994
4995 // Copy it
4996 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4997
4998 // Convert
4999 trk_copy->is_route = !trk_copy->is_route;
5000
5001 // ATM can't set name to self - so must create temporary copy
5002 gchar *name = g_strdup ( trk_copy->name );
5003
5004 // Delete old one and then add new one
5005 if ( trk->is_route ) {
5006 vik_trw_layer_delete_route ( vtl, trk );
5007 vik_trw_layer_add_track ( vtl, name, trk_copy );
5008 }
5009 else {
5010 // Extra route conversion bits...
5011 vik_track_merge_segments ( trk_copy );
5012 vik_track_to_routepoints ( trk_copy );
5013
5014 vik_trw_layer_delete_track ( vtl, trk );
5015 vik_trw_layer_add_route ( vtl, name, trk_copy );
5016 }
5017 g_free ( name );
5018
5019 // Update in case color of track / route changes when moving between sublayers
5020 vik_layer_emit_update ( VIK_LAYER(vtl) );
5021}
5022
5023static void trw_layer_anonymize_times ( menu_array_sublayer values )
5024{
5025 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5026 VikTrack *track;
5027 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5028 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5029 else
5030 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5031
5032 if ( track )
5033 vik_track_anonymize_times ( track );
5034}
5035
5036static void trw_layer_extend_track_end ( menu_array_sublayer values )
5037{
5038 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5039 VikTrack *track;
5040 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5041 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5042 else
5043 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5044
5045 if ( !track )
5046 return;
5047
5048 vtl->current_track = track;
5049 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, track->is_route ? TOOL_CREATE_ROUTE : TOOL_CREATE_TRACK);
5050
5051 if ( track->trackpoints )
5052 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5053}
5054
5055/**
5056 * extend a track using route finder
5057 */
5058static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5059{
5060 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5061 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5062 if ( !track )
5063 return;
5064 if ( !track->trackpoints )
5065 return;
5066
5067 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5068 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5069 vtl->route_finder_current_track = track;
5070 vtl->route_finder_started = TRUE;
5071
5072 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5073}
5074
5075/**
5076 *
5077 */
5078static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5079{
5080 // If have a vlp then perform a basic test to see if any DEM info available...
5081 if ( vlp ) {
5082 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5083
5084 if ( !g_list_length(dems) ) {
5085 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5086 return FALSE;
5087 }
5088 }
5089 return TRUE;
5090}
5091
5092/**
5093 * apply_dem_data_common:
5094 *
5095 * A common function for applying the DEM values and reporting the results.
5096 */
5097static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5098{
5099 if ( !trw_layer_dem_test ( vtl, vlp ) )
5100 return;
5101
5102 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5103 // Inform user how much was changed
5104 gchar str[64];
5105 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5106 g_snprintf(str, 64, tmp_str, changed);
5107 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5108}
5109
5110static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5111{
5112 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5113 VikTrack *track;
5114 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5115 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5116 else
5117 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5118
5119 if ( track )
5120 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5121}
5122
5123static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5124{
5125 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5126 VikTrack *track;
5127 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5128 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5129 else
5130 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5131
5132 if ( track )
5133 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5134}
5135
5136/**
5137 * smooth_it:
5138 *
5139 * A common function for applying the elevation smoothing and reporting the results.
5140 */
5141static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5142{
5143 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5144 // Inform user how much was changed
5145 gchar str[64];
5146 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5147 g_snprintf(str, 64, tmp_str, changed);
5148 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5149}
5150
5151/**
5152 *
5153 */
5154static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5155{
5156 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5157 VikTrack *track;
5158 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5159 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5160 else
5161 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5162
5163 if ( !track )
5164 return;
5165
5166 smooth_it ( vtl, track, FALSE );
5167}
5168
5169static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5170{
5171 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5172 VikTrack *track;
5173 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5174 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5175 else
5176 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5177
5178 if ( !track )
5179 return;
5180
5181 smooth_it ( vtl, track, TRUE );
5182}
5183
5184/**
5185 * Commonal helper function
5186 */
5187static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5188{
5189 gchar str[64];
5190 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5191 g_snprintf(str, 64, tmp_str, changed);
5192 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5193}
5194
5195static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5196{
5197 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5198 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5199
5200 if ( !trw_layer_dem_test ( vtl, vlp ) )
5201 return;
5202
5203 gint changed = 0;
5204 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5205 // Single Waypoint
5206 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5207 if ( wp )
5208 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5209 }
5210 else {
5211 // All waypoints
5212 GHashTableIter iter;
5213 gpointer key, value;
5214
5215 g_hash_table_iter_init ( &iter, vtl->waypoints );
5216 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5217 VikWaypoint *wp = VIK_WAYPOINT(value);
5218 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5219 }
5220 }
5221 wp_changed_message ( vtl, changed );
5222}
5223
5224static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5225{
5226 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5227 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5228
5229 if ( !trw_layer_dem_test ( vtl, vlp ) )
5230 return;
5231
5232 gint changed = 0;
5233 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5234 // Single Waypoint
5235 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5236 if ( wp )
5237 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5238 }
5239 else {
5240 // All waypoints
5241 GHashTableIter iter;
5242 gpointer key, value;
5243
5244 g_hash_table_iter_init ( &iter, vtl->waypoints );
5245 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5246 VikWaypoint *wp = VIK_WAYPOINT(value);
5247 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5248 }
5249 }
5250 wp_changed_message ( vtl, changed );
5251}
5252
5253static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5254{
5255 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5256 VikTrack *track;
5257 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5258 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5259 else
5260 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5261
5262 if ( !track )
5263 return;
5264 if ( !track->trackpoints )
5265 return;
5266 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5267}
5268
5269static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5270{
5271 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5272 VikTrack *track;
5273 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5274 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5275 else
5276 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5277
5278 if ( !track )
5279 return;
5280
5281 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5282 if ( !vtp )
5283 return;
5284 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5285}
5286
5287static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5288{
5289 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5290 VikTrack *track;
5291 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5292 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5293 else
5294 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5295
5296 if ( !track )
5297 return;
5298
5299 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5300 if ( !vtp )
5301 return;
5302 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5303}
5304
5305static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5306{
5307 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5308 VikTrack *track;
5309 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5310 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5311 else
5312 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5313
5314 if ( !track )
5315 return;
5316
5317 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5318 if ( !vtp )
5319 return;
5320 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5321}
5322
5323/*
5324 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5325 */
5326static void trw_layer_auto_track_view ( menu_array_sublayer values )
5327{
5328 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5329 VikTrack *trk;
5330 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5331 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5332 else
5333 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5334
5335 if ( trk && trk->trackpoints )
5336 {
5337 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5338 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5339 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5340 if ( values[MA_VLP] )
5341 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5342 else
5343 vik_layer_emit_update ( VIK_LAYER(vtl) );
5344 }
5345}
5346
5347/*
5348 * Refine the selected track/route with a routing engine.
5349 * The routing engine is selected by the user, when requestiong the job.
5350 */
5351static void trw_layer_route_refine ( menu_array_sublayer values )
5352{
5353 static gint last_engine = 0;
5354 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5355 VikTrack *trk;
5356
5357 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5358 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5359 else
5360 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5361
5362 if ( trk && trk->trackpoints )
5363 {
5364 /* Check size of the route */
5365 int nb = vik_track_get_tp_count(trk);
5366 if (nb > 100) {
5367 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5368 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5369 GTK_MESSAGE_WARNING,
5370 GTK_BUTTONS_OK_CANCEL,
5371 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5372 nb);
5373 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5374 gtk_widget_destroy ( dialog );
5375 if (response != GTK_RESPONSE_OK )
5376 return;
5377 }
5378 /* Select engine from dialog */
5379 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5380 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5381 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5382 GTK_STOCK_CANCEL,
5383 GTK_RESPONSE_REJECT,
5384 GTK_STOCK_OK,
5385 GTK_RESPONSE_ACCEPT,
5386 NULL);
5387 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5388 gtk_widget_show_all(label);
5389
5390 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5391
5392 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5393 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5394 gtk_widget_show_all(combo);
5395
5396 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5397
5398 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5399
5400 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5401 {
5402 /* Dialog validated: retrieve selected engine and do the job */
5403 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5404 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5405
5406 /* Change cursor */
5407 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5408
5409 /* Force saving track */
5410 /* FIXME: remove or rename this hack */
5411 vtl->route_finder_check_added_track = TRUE;
5412
5413 /* the job */
5414 vik_routing_engine_refine (routing, vtl, trk);
5415
5416 /* FIXME: remove or rename this hack */
5417 if ( vtl->route_finder_added_track )
5418 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5419
5420 vtl->route_finder_added_track = NULL;
5421 vtl->route_finder_check_added_track = FALSE;
5422
5423 vik_layer_emit_update ( VIK_LAYER(vtl) );
5424
5425 /* Restore cursor */
5426 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5427 }
5428 gtk_widget_destroy ( dialog );
5429 }
5430}
5431
5432static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5433{
5434 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5435 trw_layer_tpwin_init ( vtl );
5436}
5437
5438/*************************************
5439 * merge/split by time routines
5440 *************************************/
5441
5442/* called for each key in track hash table.
5443 * If the current track has the same time stamp type, add it to the result,
5444 * except the one pointed by "exclude".
5445 * set exclude to NULL if there is no exclude to check.
5446 * Note that the result is in reverse (for performance reasons).
5447 */
5448typedef struct {
5449 GList **result;
5450 GList *exclude;
5451 gboolean with_timestamps;
5452} twt_udata;
5453static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5454{
5455 twt_udata *user_data = udata;
5456 VikTrackpoint *p1, *p2;
5457 VikTrack *trk = VIK_TRACK(value);
5458 if (trk == (VikTrack *)user_data->exclude) {
5459 return;
5460 }
5461
5462 if (trk->trackpoints) {
5463 p1 = vik_track_get_tp_first(trk);
5464 p2 = vik_track_get_tp_last(trk);
5465
5466 if ( user_data->with_timestamps ) {
5467 if (!p1->has_timestamp || !p2->has_timestamp) {
5468 return;
5469 }
5470 }
5471 else {
5472 // Don't add tracks with timestamps when getting non timestamp tracks
5473 if (p1->has_timestamp || p2->has_timestamp) {
5474 return;
5475 }
5476 }
5477 }
5478
5479 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5480}
5481
5482/* called for each key in track hash table. if original track user_data[1] is close enough
5483 * to the passed one, add it to list in user_data[0]
5484 */
5485static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5486{
5487 time_t t1, t2;
5488 VikTrackpoint *p1, *p2;
5489 VikTrack *trk = VIK_TRACK(value);
5490
5491 GList **nearby_tracks = ((gpointer *)user_data)[0];
5492 GList *tpoints = ((gpointer *)user_data)[1];
5493
5494 /* outline:
5495 * detect reasons for not merging, and return
5496 * if no reason is found not to merge, then do it.
5497 */
5498
5499 // Exclude the original track from the compiled list
5500 if (trk->trackpoints == tpoints) {
5501 return;
5502 }
5503
5504 t1 = vik_track_get_tp_first(trk)->timestamp;
5505 t2 = vik_track_get_tp_last(trk)->timestamp;
5506
5507 if (trk->trackpoints) {
5508 p1 = vik_track_get_tp_first(trk);
5509 p2 = vik_track_get_tp_last(trk);
5510
5511 if (!p1->has_timestamp || !p2->has_timestamp) {
5512 //g_print("no timestamp\n");
5513 return;
5514 }
5515
5516 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5517 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5518 if (! (abs(t1 - p2->timestamp) < threshold ||
5519 /* p1 p2 t1 t2 */
5520 abs(p1->timestamp - t2) < threshold)
5521 /* t1 t2 p1 p2 */
5522 ) {
5523 return;
5524 }
5525 }
5526
5527 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5528}
5529
5530/* comparison function used to sort tracks; a and b are hash table keys */
5531/* Not actively used - can be restored if needed
5532static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5533{
5534 GHashTable *tracks = user_data;
5535 time_t t1, t2;
5536
5537 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5538 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5539
5540 if (t1 < t2) return -1;
5541 if (t1 > t2) return 1;
5542 return 0;
5543}
5544*/
5545
5546/* comparison function used to sort trackpoints */
5547static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5548{
5549 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5550
5551 if (t1 < t2) return -1;
5552 if (t1 > t2) return 1;
5553 return 0;
5554}
5555
5556/**
5557 * comparison function which can be used to sort tracks or waypoints by name
5558 */
5559static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5560{
5561 const gchar* namea = (const gchar*) a;
5562 const gchar* nameb = (const gchar*) b;
5563 if ( namea == NULL || nameb == NULL)
5564 return 0;
5565 else
5566 // Same sort method as used in the vik_treeview_*_alphabetize functions
5567 return strcmp ( namea, nameb );
5568}
5569
5570/**
5571 * Attempt to merge selected track with other tracks specified by the user
5572 * Tracks to merge with must be of the same 'type' as the selected track -
5573 * either all with timestamps, or all without timestamps
5574 */
5575static void trw_layer_merge_with_other ( menu_array_sublayer values )
5576{
5577 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5578 GList *other_tracks = NULL;
5579 GHashTable *ght_tracks;
5580 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5581 ght_tracks = vtl->routes;
5582 else
5583 ght_tracks = vtl->tracks;
5584
5585 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5586
5587 if ( !track )
5588 return;
5589
5590 if ( !track->trackpoints )
5591 return;
5592
5593 twt_udata udata;
5594 udata.result = &other_tracks;
5595 udata.exclude = track->trackpoints;
5596 // Allow merging with 'similar' time type time tracks
5597 // i.e. either those times, or those without
5598 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5599
5600 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5601 other_tracks = g_list_reverse(other_tracks);
5602
5603 if ( !other_tracks ) {
5604 if ( udata.with_timestamps )
5605 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5606 else
5607 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5608 return;
5609 }
5610
5611 // Sort alphabetically for user presentation
5612 // Convert into list of names for usage with dialog function
5613 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5614 GList *other_tracks_names = NULL;
5615 GList *iter = g_list_first ( other_tracks );
5616 while ( iter ) {
5617 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5618 iter = g_list_next ( iter );
5619 }
5620
5621 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5622
5623 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5624 other_tracks_names,
5625 TRUE,
5626 _("Merge with..."),
5627 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5628 g_list_free(other_tracks);
5629 g_list_free(other_tracks_names);
5630
5631 if (merge_list)
5632 {
5633 GList *l;
5634 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5635 VikTrack *merge_track;
5636 if ( track->is_route )
5637 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5638 else
5639 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5640
5641 if (merge_track) {
5642 vik_track_steal_and_append_trackpoints ( track, merge_track );
5643 if ( track->is_route )
5644 vik_trw_layer_delete_route (vtl, merge_track);
5645 else
5646 vik_trw_layer_delete_track (vtl, merge_track);
5647 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5648 }
5649 }
5650 for (l = merge_list; l != NULL; l = g_list_next(l))
5651 g_free(l->data);
5652 g_list_free(merge_list);
5653
5654 vik_layer_emit_update( VIK_LAYER(vtl) );
5655 }
5656}
5657
5658// c.f. trw_layer_sorted_track_id_by_name_list
5659// but don't add the specified track to the list (normally current track)
5660static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5661{
5662 twt_udata *user_data = udata;
5663
5664 // Skip self
5665 if (trk->trackpoints == user_data->exclude) {
5666 return;
5667 }
5668
5669 // Sort named list alphabetically
5670 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5671}
5672
5673/**
5674 * Join - this allows combining 'tracks' and 'track routes'
5675 * i.e. doesn't care about whether tracks have consistent timestamps
5676 * ATM can only append one track at a time to the currently selected track
5677 */
5678static void trw_layer_append_track ( menu_array_sublayer values )
5679{
5680
5681 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5682 VikTrack *trk;
5683 GHashTable *ght_tracks;
5684 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5685 ght_tracks = vtl->routes;
5686 else
5687 ght_tracks = vtl->tracks;
5688
5689 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5690
5691 if ( !trk )
5692 return;
5693
5694 GList *other_tracks_names = NULL;
5695
5696 // Sort alphabetically for user presentation
5697 // Convert into list of names for usage with dialog function
5698 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5699 twt_udata udata;
5700 udata.result = &other_tracks_names;
5701 udata.exclude = trk->trackpoints;
5702
5703 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5704
5705 // Note the limit to selecting one track only
5706 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5707 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5708 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5709 other_tracks_names,
5710 FALSE,
5711 trk->is_route ? _("Append Route"): _("Append Track"),
5712 trk->is_route ? _("Select the route to append after the current route") :
5713 _("Select the track to append after the current track") );
5714
5715 g_list_free(other_tracks_names);
5716
5717 // It's a list, but shouldn't contain more than one other track!
5718 if ( append_list ) {
5719 GList *l;
5720 for (l = append_list; l != NULL; l = g_list_next(l)) {
5721 // TODO: at present this uses the first track found by name,
5722 // which with potential multiple same named tracks may not be the one selected...
5723 VikTrack *append_track;
5724 if ( trk->is_route )
5725 append_track = vik_trw_layer_get_route ( vtl, l->data );
5726 else
5727 append_track = vik_trw_layer_get_track ( vtl, l->data );
5728
5729 if ( append_track ) {
5730 vik_track_steal_and_append_trackpoints ( trk, append_track );
5731 if ( trk->is_route )
5732 vik_trw_layer_delete_route (vtl, append_track);
5733 else
5734 vik_trw_layer_delete_track (vtl, append_track);
5735 }
5736 }
5737 for (l = append_list; l != NULL; l = g_list_next(l))
5738 g_free(l->data);
5739 g_list_free(append_list);
5740
5741 vik_layer_emit_update( VIK_LAYER(vtl) );
5742 }
5743}
5744
5745/**
5746 * Very similar to trw_layer_append_track for joining
5747 * but this allows selection from the 'other' list
5748 * If a track is selected, then is shows routes and joins the selected one
5749 * If a route is selected, then is shows tracks and joins the selected one
5750 */
5751static void trw_layer_append_other ( menu_array_sublayer values )
5752{
5753
5754 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5755 VikTrack *trk;
5756 GHashTable *ght_mykind, *ght_others;
5757 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5758 ght_mykind = vtl->routes;
5759 ght_others = vtl->tracks;
5760 }
5761 else {
5762 ght_mykind = vtl->tracks;
5763 ght_others = vtl->routes;
5764 }
5765
5766 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5767
5768 if ( !trk )
5769 return;
5770
5771 GList *other_tracks_names = NULL;
5772
5773 // Sort alphabetically for user presentation
5774 // Convert into list of names for usage with dialog function
5775 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5776 twt_udata udata;
5777 udata.result = &other_tracks_names;
5778 udata.exclude = trk->trackpoints;
5779
5780 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5781
5782 // Note the limit to selecting one track only
5783 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5784 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5785 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5786 other_tracks_names,
5787 FALSE,
5788 trk->is_route ? _("Append Track"): _("Append Route"),
5789 trk->is_route ? _("Select the track to append after the current route") :
5790 _("Select the route to append after the current track") );
5791
5792 g_list_free(other_tracks_names);
5793
5794 // It's a list, but shouldn't contain more than one other track!
5795 if ( append_list ) {
5796 GList *l;
5797 for (l = append_list; l != NULL; l = g_list_next(l)) {
5798 // TODO: at present this uses the first track found by name,
5799 // which with potential multiple same named tracks may not be the one selected...
5800
5801 // Get FROM THE OTHER TYPE list
5802 VikTrack *append_track;
5803 if ( trk->is_route )
5804 append_track = vik_trw_layer_get_track ( vtl, l->data );
5805 else
5806 append_track = vik_trw_layer_get_route ( vtl, l->data );
5807
5808 if ( append_track ) {
5809
5810 if ( !append_track->is_route &&
5811 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5812 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5813
5814 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5815 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5816 vik_track_merge_segments ( append_track );
5817 vik_track_to_routepoints ( append_track );
5818 }
5819 else {
5820 break;
5821 }
5822 }
5823
5824 vik_track_steal_and_append_trackpoints ( trk, append_track );
5825
5826 // Delete copied which is FROM THE OTHER TYPE list
5827 if ( trk->is_route )
5828 vik_trw_layer_delete_track (vtl, append_track);
5829 else
5830 vik_trw_layer_delete_route (vtl, append_track);
5831 }
5832 }
5833 for (l = append_list; l != NULL; l = g_list_next(l))
5834 g_free(l->data);
5835 g_list_free(append_list);
5836 vik_layer_emit_update( VIK_LAYER(vtl) );
5837 }
5838}
5839
5840/* merge by segments */
5841static void trw_layer_merge_by_segment ( menu_array_sublayer values )
5842{
5843 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5844 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5845 guint segments = vik_track_merge_segments ( trk );
5846 // NB currently no need to redraw as segments not actually shown on the display
5847 // However inform the user of what happened:
5848 gchar str[64];
5849 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5850 g_snprintf(str, 64, tmp_str, segments);
5851 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5852}
5853
5854/* merge by time routine */
5855static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
5856{
5857 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5858
5859 //time_t t1, t2;
5860
5861 GList *tracks_with_timestamp = NULL;
5862 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5863 if (orig_trk->trackpoints &&
5864 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
5865 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5866 return;
5867 }
5868
5869 twt_udata udata;
5870 udata.result = &tracks_with_timestamp;
5871 udata.exclude = orig_trk->trackpoints;
5872 udata.with_timestamps = TRUE;
5873 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5874 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5875
5876 if (!tracks_with_timestamp) {
5877 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5878 return;
5879 }
5880 g_list_free(tracks_with_timestamp);
5881
5882 static guint threshold_in_minutes = 1;
5883 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5884 _("Merge Threshold..."),
5885 _("Merge when time between tracks less than:"),
5886 &threshold_in_minutes)) {
5887 return;
5888 }
5889
5890 // keep attempting to merge all tracks until no merges within the time specified is possible
5891 gboolean attempt_merge = TRUE;
5892 GList *nearby_tracks = NULL;
5893 GList *trps;
5894 static gpointer params[3];
5895
5896 while ( attempt_merge ) {
5897
5898 // Don't try again unless tracks have changed
5899 attempt_merge = FALSE;
5900
5901 trps = orig_trk->trackpoints;
5902 if ( !trps )
5903 return;
5904
5905 if (nearby_tracks) {
5906 g_list_free(nearby_tracks);
5907 nearby_tracks = NULL;
5908 }
5909
5910 params[0] = &nearby_tracks;
5911 params[1] = (gpointer)trps;
5912 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5913
5914 /* get a list of adjacent-in-time tracks */
5915 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5916
5917 /* merge them */
5918 GList *l = nearby_tracks;
5919 while ( l ) {
5920 /* remove trackpoints from merged track, delete track */
5921 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5922 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5923
5924 // Tracks have changed, therefore retry again against all the remaining tracks
5925 attempt_merge = TRUE;
5926
5927 l = g_list_next(l);
5928 }
5929
5930 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5931 }
5932
5933 g_list_free(nearby_tracks);
5934
5935 vik_layer_emit_update( VIK_LAYER(vtl) );
5936}
5937
5938/**
5939 * Split a track at the currently selected trackpoint
5940 */
5941static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5942{
5943 if ( !vtl->current_tpl )
5944 return;
5945
5946 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5947 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5948 if ( name ) {
5949 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5950 GList *newglist = g_list_alloc ();
5951 newglist->prev = NULL;
5952 newglist->next = vtl->current_tpl->next;
5953 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5954 tr->trackpoints = newglist;
5955
5956 vtl->current_tpl->next->prev = newglist; /* end old track here */
5957 vtl->current_tpl->next = NULL;
5958
5959 // Bounds of the selected track changed due to the split
5960 vik_track_calculate_bounds ( vtl->current_tp_track );
5961
5962 vtl->current_tpl = newglist; /* change tp to first of new track. */
5963 vtl->current_tp_track = tr;
5964
5965 if ( tr->is_route )
5966 vik_trw_layer_add_route ( vtl, name, tr );
5967 else
5968 vik_trw_layer_add_track ( vtl, name, tr );
5969
5970 // Bounds of the new track created by the split
5971 vik_track_calculate_bounds ( tr );
5972
5973 trku_udata udata;
5974 udata.trk = tr;
5975 udata.uuid = NULL;
5976
5977 // Also need id of newly created track
5978 gpointer *trkf;
5979 if ( tr->is_route )
5980 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5981 else
5982 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5983
5984 if ( trkf && udata.uuid )
5985 vtl->current_tp_id = udata.uuid;
5986 else
5987 vtl->current_tp_id = NULL;
5988
5989 vik_layer_emit_update(VIK_LAYER(vtl));
5990 }
5991 g_free ( name );
5992 }
5993}
5994
5995/* split by time routine */
5996static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
5997{
5998 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5999 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6000 GList *trps = track->trackpoints;
6001 GList *iter;
6002 GList *newlists = NULL;
6003 GList *newtps = NULL;
6004 static guint thr = 1;
6005
6006 time_t ts, prev_ts;
6007
6008 if ( !trps )
6009 return;
6010
6011 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6012 _("Split Threshold..."),
6013 _("Split when time between trackpoints exceeds:"),
6014 &thr)) {
6015 return;
6016 }
6017
6018 /* iterate through trackpoints, and copy them into new lists without touching original list */
6019 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6020 iter = trps;
6021
6022 while (iter) {
6023 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6024
6025 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6026 if (ts < prev_ts) {
6027 gchar tmp_str[64];
6028 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6029 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6030 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6031 tmp_str ) ) {
6032 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6033 }
6034 return;
6035 }
6036
6037 if (ts - prev_ts > thr*60) {
6038 /* flush accumulated trackpoints into new list */
6039 newlists = g_list_append(newlists, g_list_reverse(newtps));
6040 newtps = NULL;
6041 }
6042
6043 /* accumulate trackpoint copies in newtps, in reverse order */
6044 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6045 prev_ts = ts;
6046 iter = g_list_next(iter);
6047 }
6048 if (newtps) {
6049 newlists = g_list_append(newlists, g_list_reverse(newtps));
6050 }
6051
6052 /* put lists of trackpoints into tracks */
6053 iter = newlists;
6054 // Only bother updating if the split results in new tracks
6055 if (g_list_length (newlists) > 1) {
6056 while (iter) {
6057 gchar *new_tr_name;
6058 VikTrack *tr;
6059
6060 tr = vik_track_copy ( track, FALSE );
6061 tr->trackpoints = (GList *)(iter->data);
6062
6063 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6064 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6065 g_free ( new_tr_name );
6066 vik_track_calculate_bounds ( tr );
6067 iter = g_list_next(iter);
6068 }
6069 // Remove original track and then update the display
6070 vik_trw_layer_delete_track (vtl, track);
6071 vik_layer_emit_update(VIK_LAYER(vtl));
6072 }
6073 g_list_free(newlists);
6074}
6075
6076/**
6077 * Split a track by the number of points as specified by the user
6078 */
6079static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6080{
6081 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6082 VikTrack *track;
6083 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6084 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6085 else
6086 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6087
6088 if ( !track )
6089 return;
6090
6091 // Check valid track
6092 GList *trps = track->trackpoints;
6093 if ( !trps )
6094 return;
6095
6096 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6097 _("Split Every Nth Point"),
6098 _("Split on every Nth point:"),
6099 250, // Default value as per typical limited track capacity of various GPS devices
6100 2, // Min
6101 65536, // Max
6102 5); // Step
6103 // Was a valid number returned?
6104 if (!points)
6105 return;
6106
6107 // Now split...
6108 GList *iter;
6109 GList *newlists = NULL;
6110 GList *newtps = NULL;
6111 gint count = 0;
6112 iter = trps;
6113
6114 while (iter) {
6115 /* accumulate trackpoint copies in newtps, in reverse order */
6116 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6117 count++;
6118 if (count >= points) {
6119 /* flush accumulated trackpoints into new list */
6120 newlists = g_list_append(newlists, g_list_reverse(newtps));
6121 newtps = NULL;
6122 count = 0;
6123 }
6124 iter = g_list_next(iter);
6125 }
6126
6127 // If there is a remaining chunk put that into the new split list
6128 // This may well be the whole track if no split points were encountered
6129 if (newtps) {
6130 newlists = g_list_append(newlists, g_list_reverse(newtps));
6131 }
6132
6133 /* put lists of trackpoints into tracks */
6134 iter = newlists;
6135 // Only bother updating if the split results in new tracks
6136 if (g_list_length (newlists) > 1) {
6137 while (iter) {
6138 gchar *new_tr_name;
6139 VikTrack *tr;
6140
6141 tr = vik_track_copy ( track, FALSE );
6142 tr->trackpoints = (GList *)(iter->data);
6143
6144 if ( track->is_route ) {
6145 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6146 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6147 }
6148 else {
6149 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6150 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6151 }
6152 g_free ( new_tr_name );
6153 vik_track_calculate_bounds ( tr );
6154
6155 iter = g_list_next(iter);
6156 }
6157 // Remove original track and then update the display
6158 if ( track->is_route )
6159 vik_trw_layer_delete_route (vtl, track);
6160 else
6161 vik_trw_layer_delete_track (vtl, track);
6162 vik_layer_emit_update(VIK_LAYER(vtl));
6163 }
6164 g_list_free(newlists);
6165}
6166
6167/**
6168 * Split a track at the currently selected trackpoint
6169 */
6170static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6171{
6172 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6173 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6174 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6175}
6176
6177/**
6178 * Split a track by its segments
6179 * Routes do not have segments so don't call this for routes
6180 */
6181static void trw_layer_split_segments ( menu_array_sublayer values )
6182{
6183 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6184 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6185
6186 if ( !trk )
6187 return;
6188
6189 guint ntracks;
6190
6191 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6192 gchar *new_tr_name;
6193 guint i;
6194 for ( i = 0; i < ntracks; i++ ) {
6195 if ( tracks[i] ) {
6196 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6197 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6198 g_free ( new_tr_name );
6199 }
6200 }
6201 if ( tracks ) {
6202 g_free ( tracks );
6203 // Remove original track
6204 vik_trw_layer_delete_track ( vtl, trk );
6205 vik_layer_emit_update ( VIK_LAYER(vtl) );
6206 }
6207 else {
6208 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6209 }
6210}
6211/* end of split/merge routines */
6212
6213static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6214{
6215 GList *new_tpl;
6216
6217 // Find available adjacent trackpoint
6218 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6219 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6220 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6221
6222 // Delete current trackpoint
6223 vik_trackpoint_free ( vtl->current_tpl->data );
6224 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6225
6226 // Set to current to the available adjacent trackpoint
6227 vtl->current_tpl = new_tpl;
6228
6229 if ( vtl->current_tp_track ) {
6230 vik_track_calculate_bounds ( vtl->current_tp_track );
6231 }
6232 }
6233 else {
6234 // Delete current trackpoint
6235 vik_trackpoint_free ( vtl->current_tpl->data );
6236 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6237 trw_layer_cancel_current_tp ( vtl, FALSE );
6238 }
6239}
6240
6241/**
6242 * Delete the selected point
6243 */
6244static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6245{
6246 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6247 VikTrack *trk;
6248 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6249 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6250 else
6251 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6252
6253 if ( !trk )
6254 return;
6255
6256 if ( !vtl->current_tpl )
6257 return;
6258
6259 trw_layer_trackpoint_selected_delete ( vtl, trk );
6260
6261 // Track has been updated so update tps:
6262 trw_layer_cancel_tps_of_track ( vtl, trk );
6263
6264 vik_layer_emit_update ( VIK_LAYER(vtl) );
6265}
6266
6267/**
6268 * Delete adjacent track points at the same position
6269 * AKA Delete Dulplicates on the Properties Window
6270 */
6271static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6272{
6273 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6274 VikTrack *trk;
6275 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6276 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6277 else
6278 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6279
6280 if ( !trk )
6281 return;
6282
6283 gulong removed = vik_track_remove_dup_points ( trk );
6284
6285 // Track has been updated so update tps:
6286 trw_layer_cancel_tps_of_track ( vtl, trk );
6287
6288 // Inform user how much was deleted as it's not obvious from the normal view
6289 gchar str[64];
6290 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6291 g_snprintf(str, 64, tmp_str, removed);
6292 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6293
6294 vik_layer_emit_update ( VIK_LAYER(vtl) );
6295}
6296
6297/**
6298 * Delete adjacent track points with the same timestamp
6299 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6300 */
6301static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6302{
6303 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6304 VikTrack *trk;
6305 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6306 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6307 else
6308 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6309
6310 if ( !trk )
6311 return;
6312
6313 gulong removed = vik_track_remove_same_time_points ( trk );
6314
6315 // Track has been updated so update tps:
6316 trw_layer_cancel_tps_of_track ( vtl, trk );
6317
6318 // Inform user how much was deleted as it's not obvious from the normal view
6319 gchar str[64];
6320 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6321 g_snprintf(str, 64, tmp_str, removed);
6322 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6323
6324 vik_layer_emit_update ( VIK_LAYER(vtl) );
6325}
6326
6327/**
6328 * Insert a point
6329 */
6330static void trw_layer_insert_point_after ( menu_array_sublayer values )
6331{
6332 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6333 VikTrack *track;
6334 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6335 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6336 else
6337 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6338
6339 if ( ! track )
6340 return;
6341
6342 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6343
6344 vik_layer_emit_update ( VIK_LAYER(vtl) );
6345}
6346
6347static void trw_layer_insert_point_before ( menu_array_sublayer values )
6348{
6349 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6350 VikTrack *track;
6351 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6352 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6353 else
6354 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6355
6356 if ( ! track )
6357 return;
6358
6359 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6360
6361 vik_layer_emit_update ( VIK_LAYER(vtl) );
6362}
6363
6364/**
6365 * Reverse a track
6366 */
6367static void trw_layer_reverse ( menu_array_sublayer values )
6368{
6369 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6370 VikTrack *track;
6371 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6372 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6373 else
6374 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6375
6376 if ( ! track )
6377 return;
6378
6379 vik_track_reverse ( track );
6380
6381 vik_layer_emit_update ( VIK_LAYER(vtl) );
6382}
6383
6384/**
6385 * Similar to trw_layer_enum_item, but this uses a sorted method
6386 */
6387/* Currently unused
6388static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6389{
6390 GList **list = (GList**)udata;
6391 // *list = g_list_prepend(*all, key); //unsorted method
6392 // Sort named list alphabetically
6393 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6394}
6395*/
6396
6397/**
6398 * Now Waypoint specific sort
6399 */
6400static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6401{
6402 GList **list = (GList**)udata;
6403 // Sort named list alphabetically
6404 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6405}
6406
6407/**
6408 * Track specific sort
6409 */
6410static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6411{
6412 GList **list = (GList**)udata;
6413 // Sort named list alphabetically
6414 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6415}
6416
6417
6418typedef struct {
6419 gboolean has_same_track_name;
6420 const gchar *same_track_name;
6421} same_track_name_udata;
6422
6423static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6424{
6425 const gchar* namea = (const gchar*) aa;
6426 const gchar* nameb = (const gchar*) bb;
6427
6428 // the test
6429 gint result = strcmp ( namea, nameb );
6430
6431 if ( result == 0 ) {
6432 // Found two names the same
6433 same_track_name_udata *user_data = udata;
6434 user_data->has_same_track_name = TRUE;
6435 user_data->same_track_name = namea;
6436 }
6437
6438 // Leave ordering the same
6439 return 0;
6440}
6441
6442/**
6443 * Find out if any tracks have the same name in this hash table
6444 */
6445static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6446{
6447 // Sort items by name, then compare if any next to each other are the same
6448
6449 GList *track_names = NULL;
6450 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6451
6452 // No tracks
6453 if ( ! track_names )
6454 return FALSE;
6455
6456 same_track_name_udata udata;
6457 udata.has_same_track_name = FALSE;
6458
6459 // Use sort routine to traverse list comparing items
6460 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6461 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6462 // Still no tracks...
6463 if ( ! dummy_list )
6464 return FALSE;
6465
6466 return udata.has_same_track_name;
6467}
6468
6469/**
6470 * Force unqiue track names for the track table specified
6471 * Note the panel is a required parameter to enable the update of the names displayed
6472 * Specify if on tracks or else on routes
6473 */
6474static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6475{
6476 // . Search list for an instance of repeated name
6477 // . get track of this name
6478 // . create new name
6479 // . rename track & update equiv. treeview iter
6480 // . repeat until all different
6481
6482 same_track_name_udata udata;
6483
6484 GList *track_names = NULL;
6485 udata.has_same_track_name = FALSE;
6486 udata.same_track_name = NULL;
6487
6488 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6489
6490 // No tracks
6491 if ( ! track_names )
6492 return;
6493
6494 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6495
6496 // Still no tracks...
6497 if ( ! dummy_list1 )
6498 return;
6499
6500 while ( udata.has_same_track_name ) {
6501
6502 // Find a track with the same name
6503 VikTrack *trk;
6504 if ( ontrack )
6505 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6506 else
6507 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6508
6509 if ( ! trk ) {
6510 // Broken :(
6511 g_critical("Houston, we've had a problem.");
6512 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6513 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6514 return;
6515 }
6516
6517 // Rename it
6518 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6519 vik_track_set_name ( trk, newname );
6520
6521 trku_udata udataU;
6522 udataU.trk = trk;
6523 udataU.uuid = NULL;
6524
6525 // Need want key of it for treeview update
6526 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6527
6528 if ( trkf && udataU.uuid ) {
6529
6530 GtkTreeIter *it;
6531 if ( ontrack )
6532 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6533 else
6534 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6535
6536 if ( it ) {
6537 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6538 if ( ontrack )
6539 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6540 else
6541 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6542 }
6543 }
6544
6545 // Start trying to find same names again...
6546 track_names = NULL;
6547 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6548 udata.has_same_track_name = FALSE;
6549 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6550
6551 // No tracks any more - give up searching
6552 if ( ! dummy_list2 )
6553 udata.has_same_track_name = FALSE;
6554 }
6555
6556 // Update
6557 vik_layers_panel_emit_update ( vlp );
6558}
6559
6560static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6561{
6562 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6563 GtkTreeIter *iter;
6564
6565 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6566 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6567 iter = &(vtl->tracks_iter);
6568 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6569 break;
6570 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6571 iter = &(vtl->routes_iter);
6572 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6573 break;
6574 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6575 iter = &(vtl->waypoints_iter);
6576 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6577 break;
6578 }
6579
6580 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6581}
6582
6583static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6584{
6585 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6586 GtkTreeIter *iter;
6587
6588 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6589 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6590 iter = &(vtl->tracks_iter);
6591 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6592 break;
6593 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6594 iter = &(vtl->routes_iter);
6595 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6596 break;
6597 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6598 iter = &(vtl->waypoints_iter);
6599 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6600 break;
6601 }
6602
6603 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6604}
6605
6606/**
6607 *
6608 */
6609static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6610{
6611 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6612 GList *all = NULL;
6613
6614 // Ensure list of track names offered is unique
6615 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6616 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6617 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6618 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6619 }
6620 else
6621 return;
6622 }
6623
6624 // Sort list alphabetically for better presentation
6625 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6626
6627 if ( ! all ) {
6628 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6629 return;
6630 }
6631
6632 // Get list of items to delete from the user
6633 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6634 all,
6635 TRUE,
6636 _("Delete Selection"),
6637 _("Select tracks to delete"));
6638 g_list_free(all);
6639
6640 // Delete requested tracks
6641 // since specificly requested, IMHO no need for extra confirmation
6642 if ( delete_list ) {
6643 GList *l;
6644 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6645 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6646 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6647 }
6648 g_list_free(delete_list);
6649 vik_layer_emit_update( VIK_LAYER(vtl) );
6650 }
6651}
6652
6653/**
6654 *
6655 */
6656static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6657{
6658 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6659 GList *all = NULL;
6660
6661 // Ensure list of track names offered is unique
6662 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6663 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6664 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6665 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6666 }
6667 else
6668 return;
6669 }
6670
6671 // Sort list alphabetically for better presentation
6672 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6673
6674 if ( ! all ) {
6675 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6676 return;
6677 }
6678
6679 // Get list of items to delete from the user
6680 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6681 all,
6682 TRUE,
6683 _("Delete Selection"),
6684 _("Select routes to delete") );
6685 g_list_free(all);
6686
6687 // Delete requested routes
6688 // since specificly requested, IMHO no need for extra confirmation
6689 if ( delete_list ) {
6690 GList *l;
6691 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6692 // This deletes first route it finds of that name (but uniqueness is enforced above)
6693 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6694 }
6695 g_list_free(delete_list);
6696 vik_layer_emit_update( VIK_LAYER(vtl) );
6697 }
6698}
6699
6700typedef struct {
6701 gboolean has_same_waypoint_name;
6702 const gchar *same_waypoint_name;
6703} same_waypoint_name_udata;
6704
6705static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6706{
6707 const gchar* namea = (const gchar*) aa;
6708 const gchar* nameb = (const gchar*) bb;
6709
6710 // the test
6711 gint result = strcmp ( namea, nameb );
6712
6713 if ( result == 0 ) {
6714 // Found two names the same
6715 same_waypoint_name_udata *user_data = udata;
6716 user_data->has_same_waypoint_name = TRUE;
6717 user_data->same_waypoint_name = namea;
6718 }
6719
6720 // Leave ordering the same
6721 return 0;
6722}
6723
6724/**
6725 * Find out if any waypoints have the same name in this layer
6726 */
6727gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6728{
6729 // Sort items by name, then compare if any next to each other are the same
6730
6731 GList *waypoint_names = NULL;
6732 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6733
6734 // No waypoints
6735 if ( ! waypoint_names )
6736 return FALSE;
6737
6738 same_waypoint_name_udata udata;
6739 udata.has_same_waypoint_name = FALSE;
6740
6741 // Use sort routine to traverse list comparing items
6742 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6743 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6744 // Still no waypoints...
6745 if ( ! dummy_list )
6746 return FALSE;
6747
6748 return udata.has_same_waypoint_name;
6749}
6750
6751/**
6752 * Force unqiue waypoint names for this layer
6753 * Note the panel is a required parameter to enable the update of the names displayed
6754 */
6755static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6756{
6757 // . Search list for an instance of repeated name
6758 // . get waypoint of this name
6759 // . create new name
6760 // . rename waypoint & update equiv. treeview iter
6761 // . repeat until all different
6762
6763 same_waypoint_name_udata udata;
6764
6765 GList *waypoint_names = NULL;
6766 udata.has_same_waypoint_name = FALSE;
6767 udata.same_waypoint_name = NULL;
6768
6769 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6770
6771 // No waypoints
6772 if ( ! waypoint_names )
6773 return;
6774
6775 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6776
6777 // Still no waypoints...
6778 if ( ! dummy_list1 )
6779 return;
6780
6781 while ( udata.has_same_waypoint_name ) {
6782
6783 // Find a waypoint with the same name
6784 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6785
6786 if ( ! waypoint ) {
6787 // Broken :(
6788 g_critical("Houston, we've had a problem.");
6789 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6790 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6791 return;
6792 }
6793
6794 // Rename it
6795 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6796
6797 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6798
6799 // Start trying to find same names again...
6800 waypoint_names = NULL;
6801 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6802 udata.has_same_waypoint_name = FALSE;
6803 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6804
6805 // No waypoints any more - give up searching
6806 if ( ! dummy_list2 )
6807 udata.has_same_waypoint_name = FALSE;
6808 }
6809
6810 // Update
6811 vik_layers_panel_emit_update ( vlp );
6812}
6813
6814/**
6815 *
6816 */
6817static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
6818{
6819 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6820 GList *all = NULL;
6821
6822 // Ensure list of waypoint names offered is unique
6823 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6824 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6825 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6826 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
6827 }
6828 else
6829 return;
6830 }
6831
6832 // Sort list alphabetically for better presentation
6833 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6834 if ( ! all ) {
6835 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6836 return;
6837 }
6838
6839 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6840
6841 // Get list of items to delete from the user
6842 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6843 all,
6844 TRUE,
6845 _("Delete Selection"),
6846 _("Select waypoints to delete"));
6847 g_list_free(all);
6848
6849 // Delete requested waypoints
6850 // since specificly requested, IMHO no need for extra confirmation
6851 if ( delete_list ) {
6852 GList *l;
6853 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6854 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6855 trw_layer_delete_waypoint_by_name (vtl, l->data);
6856 }
6857 g_list_free(delete_list);
6858
6859 trw_layer_calculate_bounds_waypoints ( vtl );
6860 vik_layer_emit_update( VIK_LAYER(vtl) );
6861 }
6862
6863}
6864
6865/**
6866 *
6867 */
6868static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6869{
6870 vik_treeview_item_toggle_visible ( vt, it );
6871}
6872
6873/**
6874 *
6875 */
6876static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6877{
6878 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6879}
6880
6881/**
6882 *
6883 */
6884static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6885{
6886 wp->visible = GPOINTER_TO_INT (on_off);
6887}
6888
6889/**
6890 *
6891 */
6892static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6893{
6894 wp->visible = !wp->visible;
6895}
6896
6897/**
6898 *
6899 */
6900static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
6901{
6902 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6903 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6904 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6905 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6906 // Redraw
6907 vik_layer_emit_update ( VIK_LAYER(vtl) );
6908}
6909
6910/**
6911 *
6912 */
6913static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
6914{
6915 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6916 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6917 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6918 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6919 // Redraw
6920 vik_layer_emit_update ( VIK_LAYER(vtl) );
6921}
6922
6923/**
6924 *
6925 */
6926static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
6927{
6928 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6929 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6930 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6931 // Redraw
6932 vik_layer_emit_update ( VIK_LAYER(vtl) );
6933}
6934
6935/**
6936 *
6937 */
6938static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6939{
6940 trk->visible = GPOINTER_TO_INT (on_off);
6941}
6942
6943/**
6944 *
6945 */
6946static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6947{
6948 trk->visible = !trk->visible;
6949}
6950
6951/**
6952 *
6953 */
6954static void trw_layer_tracks_visibility_off ( menu_array_layer values )
6955{
6956 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6957 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6958 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6959 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6960 // Redraw
6961 vik_layer_emit_update ( VIK_LAYER(vtl) );
6962}
6963
6964/**
6965 *
6966 */
6967static void trw_layer_tracks_visibility_on ( menu_array_layer values )
6968{
6969 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6970 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6971 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6972 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6973 // Redraw
6974 vik_layer_emit_update ( VIK_LAYER(vtl) );
6975}
6976
6977/**
6978 *
6979 */
6980static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
6981{
6982 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6983 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6984 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6985 // Redraw
6986 vik_layer_emit_update ( VIK_LAYER(vtl) );
6987}
6988
6989/**
6990 *
6991 */
6992static void trw_layer_routes_visibility_off ( menu_array_layer values )
6993{
6994 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6995 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6996 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6997 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6998 // Redraw
6999 vik_layer_emit_update ( VIK_LAYER(vtl) );
7000}
7001
7002/**
7003 *
7004 */
7005static void trw_layer_routes_visibility_on ( menu_array_layer values )
7006{
7007 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7008 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7009 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7010 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7011 // Redraw
7012 vik_layer_emit_update ( VIK_LAYER(vtl) );
7013}
7014
7015/**
7016 *
7017 */
7018static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7019{
7020 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7021 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7022 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7023 // Redraw
7024 vik_layer_emit_update ( VIK_LAYER(vtl) );
7025}
7026
7027/**
7028 * vik_trw_layer_build_waypoint_list_t:
7029 *
7030 * Helper function to construct a list of #vik_trw_waypoint_list_t
7031 */
7032GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7033{
7034 GList *waypoints_and_layers = NULL;
7035 // build waypoints_and_layers list
7036 while ( waypoints ) {
7037 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7038 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7039 vtdl->vtl = vtl;
7040 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7041 waypoints = g_list_next ( waypoints );
7042 }
7043 return waypoints_and_layers;
7044}
7045
7046/**
7047 * trw_layer_create_waypoint_list:
7048 *
7049 * Create the latest list of waypoints with the associated layer(s)
7050 * Although this will always be from a single layer here
7051 */
7052static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7053{
7054 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7055 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7056
7057 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7058}
7059
7060/**
7061 * trw_layer_analyse_close:
7062 *
7063 * Stuff to do on dialog closure
7064 */
7065static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7066{
7067 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7068 gtk_widget_destroy ( dialog );
7069 vtl->tracks_analysis_dialog = NULL;
7070}
7071
7072/**
7073 * vik_trw_layer_build_track_list_t:
7074 *
7075 * Helper function to construct a list of #vik_trw_track_list_t
7076 */
7077GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7078{
7079 GList *tracks_and_layers = NULL;
7080 // build tracks_and_layers list
7081 while ( tracks ) {
7082 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7083 vtdl->trk = VIK_TRACK(tracks->data);
7084 vtdl->vtl = vtl;
7085 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7086 tracks = g_list_next ( tracks );
7087 }
7088 return tracks_and_layers;
7089}
7090
7091/**
7092 * trw_layer_create_track_list:
7093 *
7094 * Create the latest list of tracks with the associated layer(s)
7095 * Although this will always be from a single layer here
7096 */
7097static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7098{
7099 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7100 GList *tracks = NULL;
7101 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7102 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7103 else
7104 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7105
7106 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7107}
7108
7109static void trw_layer_tracks_stats ( menu_array_layer values )
7110{
7111 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7112 // There can only be one!
7113 if ( vtl->tracks_analysis_dialog )
7114 return;
7115
7116 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7117 VIK_LAYER(vtl)->name,
7118 VIK_LAYER(vtl),
7119 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7120 trw_layer_create_track_list,
7121 trw_layer_analyse_close );
7122}
7123
7124/**
7125 *
7126 */
7127static void trw_layer_routes_stats ( menu_array_layer values )
7128{
7129 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7130 // There can only be one!
7131 if ( vtl->tracks_analysis_dialog )
7132 return;
7133
7134 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7135 VIK_LAYER(vtl)->name,
7136 VIK_LAYER(vtl),
7137 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7138 trw_layer_create_track_list,
7139 trw_layer_analyse_close );
7140}
7141
7142static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7143{
7144 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7145 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7146 if ( wp )
7147 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7148}
7149
7150static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7151{
7152 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7153 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7154 if ( !wp )
7155 return;
7156 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7157 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7158 g_free ( webpage );
7159}
7160
7161static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7162{
7163 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7164 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7165 if ( !wp )
7166 return;
7167 if ( !strncmp(wp->comment, "http", 4) ) {
7168 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7169 } else if ( !strncmp(wp->description, "http", 4) ) {
7170 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7171 }
7172}
7173
7174static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7175{
7176 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7177 {
7178 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7179
7180 // No actual change to the name supplied
7181 if ( wp->name )
7182 if (strcmp(newname, wp->name) == 0 )
7183 return NULL;
7184
7185 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7186
7187 if ( wpf ) {
7188 // An existing waypoint has been found with the requested name
7189 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7190 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7191 newname ) )
7192 return NULL;
7193 }
7194
7195 // Update WP name and refresh the treeview
7196 vik_waypoint_set_name (wp, newname);
7197
7198 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7199 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7200
7201 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7202
7203 return newname;
7204 }
7205
7206 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7207 {
7208 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7209
7210 // No actual change to the name supplied
7211 if ( trk->name )
7212 if (strcmp(newname, trk->name) == 0)
7213 return NULL;
7214
7215 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7216
7217 if ( trkf ) {
7218 // An existing track has been found with the requested name
7219 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7220 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7221 newname ) )
7222 return NULL;
7223 }
7224 // Update track name and refresh GUI parts
7225 vik_track_set_name (trk, newname);
7226
7227 // Update any subwindows that could be displaying this track which has changed name
7228 // Only one Track Edit Window
7229 if ( l->current_tp_track == trk && l->tpwin ) {
7230 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7231 }
7232 // Property Dialog of the track
7233 vik_trw_layer_propwin_update ( trk );
7234
7235 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7236 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7237
7238 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7239
7240 return newname;
7241 }
7242
7243 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7244 {
7245 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7246
7247 // No actual change to the name supplied
7248 if ( trk->name )
7249 if (strcmp(newname, trk->name) == 0)
7250 return NULL;
7251
7252 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7253
7254 if ( trkf ) {
7255 // An existing track has been found with the requested name
7256 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7257 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7258 newname ) )
7259 return NULL;
7260 }
7261 // Update track name and refresh GUI parts
7262 vik_track_set_name (trk, newname);
7263
7264 // Update any subwindows that could be displaying this track which has changed name
7265 // Only one Track Edit Window
7266 if ( l->current_tp_track == trk && l->tpwin ) {
7267 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7268 }
7269 // Property Dialog of the track
7270 vik_trw_layer_propwin_update ( trk );
7271
7272 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7273 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7274
7275 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7276
7277 return newname;
7278 }
7279 return NULL;
7280}
7281
7282static gboolean is_valid_geocache_name ( gchar *str )
7283{
7284 gint len = strlen ( str );
7285 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]));
7286}
7287
7288static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7289{
7290 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7291 a_acquire_set_filter_track ( trk );
7292}
7293
7294#ifdef VIK_CONFIG_GOOGLE
7295static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7296{
7297 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7298 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7299}
7300
7301static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7302{
7303 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7304 if ( tr ) {
7305 gchar *escaped = uri_escape ( tr->comment );
7306 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7307 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7308 g_free ( escaped );
7309 g_free ( webpage );
7310 }
7311}
7312#endif
7313
7314/* vlp can be NULL if necessary - i.e. right-click from a tool */
7315/* viewpoint is now available instead */
7316static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7317{
7318 static menu_array_sublayer pass_along;
7319 GtkWidget *item;
7320 gboolean rv = FALSE;
7321
7322 pass_along[MA_VTL] = l;
7323 pass_along[MA_VLP] = vlp;
7324 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7325 pass_along[MA_SUBLAYER_ID] = sublayer;
7326 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7327 pass_along[MA_VVP] = vvp;
7328 pass_along[MA_TV_ITER] = iter;
7329 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7330
7331 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7332 {
7333 rv = TRUE;
7334
7335 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7337 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7338 gtk_widget_show ( item );
7339
7340 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7341 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7342 if (tr && tr->property_dialog)
7343 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7344 }
7345 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7346 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7347 if (tr && tr->property_dialog)
7348 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7349 }
7350
7351 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7353 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7354 gtk_widget_show ( item );
7355
7356 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7357 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7358 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7359 gtk_widget_show ( item );
7360
7361 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7363 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7364 gtk_widget_show ( item );
7365
7366 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7367 {
7368 // Always create separator as now there is always at least the transform menu option
7369 item = gtk_menu_item_new ();
7370 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7371 gtk_widget_show ( item );
7372
7373 /* could be a right-click using the tool */
7374 if ( vlp != NULL ) {
7375 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7376 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7378 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7379 gtk_widget_show ( item );
7380 }
7381
7382 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7383
7384 if ( wp && wp->name ) {
7385 if ( is_valid_geocache_name ( wp->name ) ) {
7386 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7387 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7388 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7389 gtk_widget_show ( item );
7390 }
7391#ifdef VIK_CONFIG_GEOTAG
7392 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7394 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7395 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7396 gtk_widget_show ( item );
7397#endif
7398 }
7399
7400 if ( wp && wp->image )
7401 {
7402 // Set up image paramater
7403 pass_along[MA_MISC] = wp->image;
7404
7405 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7406 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7407 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7408 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7409 gtk_widget_show ( item );
7410
7411#ifdef VIK_CONFIG_GEOTAG
7412 GtkWidget *geotag_submenu = gtk_menu_new ();
7413 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7414 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7415 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7416 gtk_widget_show ( item );
7417 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7418
7419 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7420 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7421 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7422 gtk_widget_show ( item );
7423
7424 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7425 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7426 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7427 gtk_widget_show ( item );
7428#endif
7429 }
7430
7431 if ( wp )
7432 {
7433 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7434 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7435 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7436 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7438 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7439 gtk_widget_show ( item );
7440 }
7441 }
7442 }
7443 }
7444
7445 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7446 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7448 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7449 gtk_widget_show ( item );
7450 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7451 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7452 gtk_widget_set_sensitive ( item, TRUE );
7453 else
7454 gtk_widget_set_sensitive ( item, FALSE );
7455
7456 // Add separator
7457 item = gtk_menu_item_new ();
7458 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7459 gtk_widget_show ( item );
7460 }
7461
7462 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7463 {
7464 rv = TRUE;
7465 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7466 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7468 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7469 gtk_widget_show ( item );
7470 }
7471
7472 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7473 {
7474 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7475 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7476 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7477 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7478 gtk_widget_show ( item );
7479
7480 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7481 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7482 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7483 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7484 gtk_widget_show ( item );
7485
7486 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7489 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7490 gtk_widget_show ( item );
7491
7492 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7493 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7495 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7496 gtk_widget_show ( item );
7497
7498 GtkWidget *vis_submenu = gtk_menu_new ();
7499 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7500 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7501 gtk_widget_show ( item );
7502 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7503
7504 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7507 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7508 gtk_widget_show ( item );
7509
7510 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7512 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7513 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7514 gtk_widget_show ( item );
7515
7516 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7517 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7519 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7520 gtk_widget_show ( item );
7521
7522 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7523 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7524 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7525 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7526 }
7527
7528 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7529 {
7530 rv = TRUE;
7531
7532 if ( l->current_track && !l->current_track->is_route ) {
7533 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7535 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7536 gtk_widget_show ( item );
7537 // Add separator
7538 item = gtk_menu_item_new ();
7539 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7540 gtk_widget_show ( item );
7541 }
7542
7543 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7544 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7546 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7547 gtk_widget_show ( item );
7548
7549 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7550 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7551 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7552 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7553 gtk_widget_show ( item );
7554 // Make it available only when a new track *not* already in progress
7555 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7556
7557 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7558 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7560 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7561 gtk_widget_show ( item );
7562
7563 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7564 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7566 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7567 gtk_widget_show ( item );
7568
7569 GtkWidget *vis_submenu = gtk_menu_new ();
7570 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7571 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7572 gtk_widget_show ( item );
7573 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7574
7575 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7576 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7577 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7578 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7579 gtk_widget_show ( item );
7580
7581 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7582 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7584 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7585 gtk_widget_show ( item );
7586
7587 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7588 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7590 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7591
7592 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7593 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7594 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7595 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7596 gtk_widget_show ( item );
7597
7598 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7600 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7601 gtk_widget_show ( item );
7602 }
7603
7604 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7605 {
7606 rv = TRUE;
7607
7608 if ( l->current_track && l->current_track->is_route ) {
7609 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7610 // Reuse finish track method
7611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7612 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7613 gtk_widget_show ( item );
7614 // Add separator
7615 item = gtk_menu_item_new ();
7616 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7617 gtk_widget_show ( item );
7618 }
7619
7620 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7621 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7623 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7624 gtk_widget_show ( item );
7625
7626 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7627 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7628 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7629 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7630 gtk_widget_show ( item );
7631 // Make it available only when a new track *not* already in progress
7632 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7633
7634 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7635 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7636 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7637 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7638 gtk_widget_show ( item );
7639
7640 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7641 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7642 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7643 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7644 gtk_widget_show ( item );
7645
7646 GtkWidget *vis_submenu = gtk_menu_new ();
7647 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7648 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7649 gtk_widget_show ( item );
7650 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7651
7652 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7653 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7655 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7656 gtk_widget_show ( item );
7657
7658 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7659 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7661 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7662 gtk_widget_show ( item );
7663
7664 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7665 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7666 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7667 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7668
7669 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7670 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7672 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7673
7674 gtk_widget_show ( item );
7675
7676 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7677 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7678 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7679 gtk_widget_show ( item );
7680 }
7681
7682
7683 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7684 GtkWidget *submenu_sort = gtk_menu_new ();
7685 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7686 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7687 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7688 gtk_widget_show ( item );
7689 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7690
7691 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7692 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7693 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7694 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7695 gtk_widget_show ( item );
7696
7697 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7698 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7700 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7701 gtk_widget_show ( item );
7702 }
7703
7704 GtkWidget *upload_submenu = gtk_menu_new ();
7705
7706 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7707 {
7708 item = gtk_menu_item_new ();
7709 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7710 gtk_widget_show ( item );
7711
7712 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7713 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7714 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7715 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7716 if ( l->current_track ) {
7717 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7718 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7719 gtk_widget_show ( item );
7720
7721 // Add separator
7722 item = gtk_menu_item_new ();
7723 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7724 gtk_widget_show ( item );
7725 }
7726
7727 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7728 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7729 else
7730 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7731 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7732 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7733 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7734 gtk_widget_show ( item );
7735
7736 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7738 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7739 gtk_widget_show ( item );
7740
7741 GtkWidget *goto_submenu;
7742 goto_submenu = gtk_menu_new ();
7743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7745 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7746 gtk_widget_show ( item );
7747 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7748
7749 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7752 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7753 gtk_widget_show ( item );
7754
7755 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7756 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7758 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7759 gtk_widget_show ( item );
7760
7761 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7762 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7763 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7764 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7765 gtk_widget_show ( item );
7766
7767 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7768 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7770 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7771 gtk_widget_show ( item );
7772
7773 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7774 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7776 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7777 gtk_widget_show ( item );
7778
7779 // Routes don't have speeds
7780 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7781 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7782 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7783 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7784 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7785 gtk_widget_show ( item );
7786 }
7787
7788 GtkWidget *combine_submenu;
7789 combine_submenu = gtk_menu_new ();
7790 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7791 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7792 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7793 gtk_widget_show ( item );
7794 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7795
7796 // Routes don't have times or segments...
7797 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7798 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7800 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7801 gtk_widget_show ( item );
7802
7803 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7805 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7806 gtk_widget_show ( item );
7807 }
7808
7809 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7811 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7812 gtk_widget_show ( item );
7813
7814 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7815 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7816 else
7817 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7820 gtk_widget_show ( item );
7821
7822 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7823 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7824 else
7825 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7827 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7828 gtk_widget_show ( item );
7829
7830 GtkWidget *split_submenu;
7831 split_submenu = gtk_menu_new ();
7832 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7833 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7834 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7835 gtk_widget_show ( item );
7836 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7837
7838 // Routes don't have times or segments...
7839 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7840 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7842 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7843 gtk_widget_show ( item );
7844
7845 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7846 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7848 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7849 gtk_widget_show ( item );
7850 }
7851
7852 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7853 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7854 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7855 gtk_widget_show ( item );
7856
7857 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7859 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7860 gtk_widget_show ( item );
7861 // Make it available only when a trackpoint is selected.
7862 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7863
7864 GtkWidget *insert_submenu = gtk_menu_new ();
7865 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7866 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7867 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7868 gtk_widget_show ( item );
7869 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7870
7871 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7873 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7874 gtk_widget_show ( item );
7875 // Make it available only when a point is selected
7876 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7877
7878 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7880 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7881 gtk_widget_show ( item );
7882 // Make it available only when a point is selected
7883 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7884
7885 GtkWidget *delete_submenu;
7886 delete_submenu = gtk_menu_new ();
7887 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7888 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7889 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7890 gtk_widget_show ( item );
7891 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7892
7893 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7896 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7897 gtk_widget_show ( item );
7898 // Make it available only when a point is selected
7899 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7900
7901 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7903 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7904 gtk_widget_show ( item );
7905
7906 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7908 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7909 gtk_widget_show ( item );
7910
7911 GtkWidget *transform_submenu;
7912 transform_submenu = gtk_menu_new ();
7913 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7915 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7916 gtk_widget_show ( item );
7917 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7918
7919 GtkWidget *dem_submenu;
7920 dem_submenu = gtk_menu_new ();
7921 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7922 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7923 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7924 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7925
7926 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7927 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7928 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7929 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7930 gtk_widget_show ( item );
7931
7932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7934 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7935 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7936 gtk_widget_show ( item );
7937
7938 GtkWidget *smooth_submenu;
7939 smooth_submenu = gtk_menu_new ();
7940 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7941 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7942 gtk_widget_show ( item );
7943 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7944
7945 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7947 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7948 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7949 gtk_widget_show ( item );
7950
7951 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7952 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7953 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7954 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7955 gtk_widget_show ( item );
7956
7957 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7958 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7959 else
7960 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7961 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7963 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7964 gtk_widget_show ( item );
7965
7966 // Routes don't have timestamps - so this is only available for tracks
7967 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7968 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7970 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7971 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7972 gtk_widget_show ( item );
7973 }
7974
7975 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7976 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7977 else
7978 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7979 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7980 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7981 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7982 gtk_widget_show ( item );
7983
7984 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7985 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7986 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7988 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7989 gtk_widget_show ( item );
7990 }
7991
7992 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7993 if ( vlp ) {
7994 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7995 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7996 else
7997 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7998 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8000 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8001 gtk_widget_show ( item );
8002 }
8003
8004 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8005 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8006 else
8007 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8010 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8011 gtk_widget_show ( item );
8012
8013 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8014 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8015 else
8016 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8017 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8019 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8020 gtk_widget_show ( item );
8021
8022 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8023 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8024 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8026 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8027 gtk_widget_show ( item );
8028 }
8029
8030 // ATM can't upload a single waypoint but can do waypoints to a GPS
8031 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8032 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8033 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8034 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8035 gtk_widget_show ( item );
8036 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8037
8038 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8039 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8041 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8042 gtk_widget_show ( item );
8043 }
8044 }
8045
8046#ifdef VIK_CONFIG_GOOGLE
8047 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8048 {
8049 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8053 gtk_widget_show ( item );
8054 }
8055#endif
8056
8057 // Some things aren't usable with routes
8058 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8059#ifdef VIK_CONFIG_OPENSTREETMAP
8060 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8061 // Convert internal pointer into track
8062 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8063 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8065 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8066 gtk_widget_show ( item );
8067#endif
8068
8069 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8070 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8072 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8073 gtk_widget_show ( item );
8074
8075 /* ATM This function is only available via the layers panel, due to needing a vlp */
8076 if ( vlp ) {
8077 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8078 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8079 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8080 if ( item ) {
8081 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8082 gtk_widget_show ( item );
8083 }
8084 }
8085
8086#ifdef VIK_CONFIG_GEOTAG
8087 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8089 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8090 gtk_widget_show ( item );
8091#endif
8092 }
8093
8094 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8095 // Only show on viewport popmenu when a trackpoint is selected
8096 if ( ! vlp && l->current_tpl ) {
8097 // Add separator
8098 item = gtk_menu_item_new ();
8099 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8100 gtk_widget_show ( item );
8101
8102 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8103 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8104 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8105 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8106 gtk_widget_show ( item );
8107 }
8108 }
8109
8110 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8111 GtkWidget *transform_submenu;
8112 transform_submenu = gtk_menu_new ();
8113 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8115 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8116 gtk_widget_show ( item );
8117 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8118
8119 GtkWidget *dem_submenu;
8120 dem_submenu = gtk_menu_new ();
8121 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8123 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8124 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8125
8126 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8128 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8129 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8130 gtk_widget_show ( item );
8131
8132 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8134 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8135 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8136 gtk_widget_show ( item );
8137 }
8138
8139 gtk_widget_show_all ( GTK_WIDGET(menu) );
8140
8141 return rv;
8142}
8143
8144// TODO: Probably better to rework this track manipulation in viktrack.c
8145static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8146{
8147 // sanity check
8148 if (!vtl->current_tpl)
8149 return;
8150
8151 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8152 VikTrackpoint *tp_other = NULL;
8153
8154 if ( before ) {
8155 if (!vtl->current_tpl->prev)
8156 return;
8157 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8158 } else {
8159 if (!vtl->current_tpl->next)
8160 return;
8161 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8162 }
8163
8164 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8165 if ( tp_other ) {
8166
8167 VikTrackpoint *tp_new = vik_trackpoint_new();
8168 struct LatLon ll_current, ll_other;
8169 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8170 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8171
8172 /* main positional interpolation */
8173 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8174 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8175
8176 /* Now other properties that can be interpolated */
8177 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8178
8179 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8180 /* Note here the division is applied to each part, then added
8181 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8182 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8183 tp_new->has_timestamp = TRUE;
8184 }
8185
8186 if (tp_current->speed != NAN && tp_other->speed != NAN)
8187 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8188
8189 /* TODO - improve interpolation of course, as it may not be correct.
8190 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8191 [similar applies if value is in radians] */
8192 if (tp_current->course != NAN && tp_other->course != NAN)
8193 tp_new->course = (tp_current->course + tp_other->course)/2;
8194
8195 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8196
8197 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8198 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8199 if ( !trk )
8200 // Otherwise try routes
8201 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8202 if ( !trk )
8203 return;
8204
8205 gint index = g_list_index ( trk->trackpoints, tp_current );
8206 if ( index > -1 ) {
8207 if ( !before )
8208 index = index + 1;
8209 // NB no recalculation of bounds since it is inserted between points
8210 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8211 }
8212 }
8213}
8214
8215static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8216{
8217 if ( vtl->tpwin )
8218 {
8219 if ( destroy)
8220 {
8221 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8222 vtl->tpwin = NULL;
8223 }
8224 else
8225 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8226 }
8227 if ( vtl->current_tpl )
8228 {
8229 vtl->current_tpl = NULL;
8230 vtl->current_tp_track = NULL;
8231 vtl->current_tp_id = NULL;
8232 vik_layer_emit_update(VIK_LAYER(vtl));
8233 }
8234}
8235
8236static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8237{
8238 g_assert ( vtl->tpwin != NULL );
8239 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8240 trw_layer_cancel_current_tp ( vtl, TRUE );
8241
8242 if ( vtl->current_tpl == NULL )
8243 return;
8244
8245 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8246 {
8247 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8248 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8249 }
8250 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8251 {
8252 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8253 if ( tr == NULL )
8254 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8255 if ( tr == NULL )
8256 return;
8257
8258 trw_layer_trackpoint_selected_delete ( vtl, tr );
8259
8260 if ( vtl->current_tpl )
8261 // Reset dialog with the available adjacent trackpoint
8262 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8263
8264 vik_layer_emit_update(VIK_LAYER(vtl));
8265 }
8266 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8267 {
8268 if ( vtl->current_tp_track )
8269 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8270 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8271 }
8272 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8273 {
8274 if ( vtl->current_tp_track )
8275 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8276 vik_layer_emit_update(VIK_LAYER(vtl));
8277 }
8278 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8279 {
8280 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8281 vik_layer_emit_update(VIK_LAYER(vtl));
8282 }
8283 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8284 vik_layer_emit_update(VIK_LAYER(vtl));
8285}
8286
8287/**
8288 * trw_layer_dialog_shift:
8289 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8290 *
8291 * Try to reposition a dialog if it's over the specified coord
8292 * so to not obscure the item of interest
8293 */
8294void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8295{
8296 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8297
8298 // Attempt force dialog to be shown so we can find out where it is more reliably...
8299 while ( gtk_events_pending() )
8300 gtk_main_iteration ();
8301
8302 // get parent window position & size
8303 gint win_pos_x, win_pos_y;
8304 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8305
8306 gint win_size_x, win_size_y;
8307 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8308
8309 // get own dialog size
8310 gint dia_size_x, dia_size_y;
8311 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8312
8313 // get own dialog position
8314 gint dia_pos_x, dia_pos_y;
8315 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8316
8317 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8318 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8319
8320 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8321
8322 gint vp_xx, vp_yy; // In viewport pixels
8323 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8324
8325 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8326
8327 gint dest_x = 0;
8328 gint dest_y = 0;
8329 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8330
8331 // Transform Viewport pixels into absolute pixels
8332 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8333 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8334
8335 // Is dialog over the point (to within an ^^ edge value)
8336 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8337 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8338
8339 if ( vertical ) {
8340 // Shift up<->down
8341 gint hh = vik_viewport_get_height ( vvp );
8342
8343 // Consider the difference in viewport to the full window
8344 gint offset_y = dest_y;
8345 // Add difference between dialog and window sizes
8346 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8347
8348 if ( vp_yy > hh/2 ) {
8349 // Point in bottom half, move window to top half
8350 gtk_window_move ( dialog, dia_pos_x, offset_y );
8351 }
8352 else {
8353 // Point in top half, move dialog down
8354 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8355 }
8356 }
8357 else {
8358 // Shift left<->right
8359 gint ww = vik_viewport_get_width ( vvp );
8360
8361 // Consider the difference in viewport to the full window
8362 gint offset_x = dest_x;
8363 // Add difference between dialog and window sizes
8364 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8365
8366 if ( vp_xx > ww/2 ) {
8367 // Point on right, move window to left
8368 gtk_window_move ( dialog, offset_x, dia_pos_y );
8369 }
8370 else {
8371 // Point on left, move right
8372 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8373 }
8374 }
8375 }
8376 }
8377 }
8378}
8379
8380static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8381{
8382 if ( ! vtl->tpwin )
8383 {
8384 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8385 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8386 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8387 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8388
8389 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8390
8391 if ( vtl->current_tpl ) {
8392 // get tp pixel position
8393 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8394
8395 // Shift up<->down to try not to obscure the trackpoint.
8396 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8397 }
8398 }
8399
8400 if ( vtl->current_tpl )
8401 if ( vtl->current_tp_track )
8402 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8403 /* set layer name and TP data */
8404}
8405
8406/***************************************************************************
8407 ** Tool code
8408 ***************************************************************************/
8409
8410/*** Utility data structures and functions ****/
8411
8412typedef struct {
8413 gint x, y;
8414 gint closest_x, closest_y;
8415 gboolean draw_images;
8416 gpointer *closest_wp_id;
8417 VikWaypoint *closest_wp;
8418 VikViewport *vvp;
8419} WPSearchParams;
8420
8421typedef struct {
8422 gint x, y;
8423 gint closest_x, closest_y;
8424 gpointer closest_track_id;
8425 VikTrackpoint *closest_tp;
8426 VikViewport *vvp;
8427 GList *closest_tpl;
8428 LatLonBBox bbox;
8429} TPSearchParams;
8430
8431static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8432{
8433 gint x, y;
8434 if ( !wp->visible )
8435 return;
8436
8437 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8438
8439 // If waypoint has an image then use the image size to select
8440 if ( params->draw_images && wp->image ) {
8441 gint slackx, slacky;
8442 slackx = wp->image_width / 2;
8443 slacky = wp->image_height / 2;
8444
8445 if ( x <= params->x + slackx && x >= params->x - slackx
8446 && y <= params->y + slacky && y >= params->y - slacky ) {
8447 params->closest_wp_id = id;
8448 params->closest_wp = wp;
8449 params->closest_x = x;
8450 params->closest_y = y;
8451 }
8452 }
8453 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8454 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8455 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8456 {
8457 params->closest_wp_id = id;
8458 params->closest_wp = wp;
8459 params->closest_x = x;
8460 params->closest_y = y;
8461 }
8462}
8463
8464static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8465{
8466 GList *tpl = t->trackpoints;
8467 VikTrackpoint *tp;
8468
8469 if ( !t->visible )
8470 return;
8471
8472 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8473 return;
8474
8475 while (tpl)
8476 {
8477 gint x, y;
8478 tp = VIK_TRACKPOINT(tpl->data);
8479
8480 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8481
8482 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8483 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8484 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8485 {
8486 params->closest_track_id = id;
8487 params->closest_tp = tp;
8488 params->closest_tpl = tpl;
8489 params->closest_x = x;
8490 params->closest_y = y;
8491 }
8492 tpl = tpl->next;
8493 }
8494}
8495
8496// ATM: Leave this as 'Track' only.
8497// Not overly bothered about having a snap to route trackpoint capability
8498static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8499{
8500 TPSearchParams params;
8501 params.x = x;
8502 params.y = y;
8503 params.vvp = vvp;
8504 params.closest_track_id = NULL;
8505 params.closest_tp = NULL;
8506 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8507 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
8508 return params.closest_tp;
8509}
8510
8511static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8512{
8513 WPSearchParams params;
8514 params.x = x;
8515 params.y = y;
8516 params.vvp = vvp;
8517 params.draw_images = vtl->drawimages;
8518 params.closest_wp = NULL;
8519 params.closest_wp_id = NULL;
8520 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
8521 return params.closest_wp;
8522}
8523
8524
8525// Some forward declarations
8526static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8527static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8528static void marker_end_move ( tool_ed_t *t );
8529//
8530
8531static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8532{
8533 if ( t->holding ) {
8534 VikCoord new_coord;
8535 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8536
8537 // Here always allow snapping back to the original location
8538 // this is useful when one decides not to move the thing afterall
8539 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8540
8541 // snap to TP
8542 if ( event->state & GDK_CONTROL_MASK )
8543 {
8544 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8545 if ( tp )
8546 new_coord = tp->coord;
8547 }
8548
8549 // snap to WP
8550 if ( event->state & GDK_SHIFT_MASK )
8551 {
8552 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8553 if ( wp )
8554 new_coord = wp->coord;
8555 }
8556
8557 gint x, y;
8558 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8559
8560 marker_moveto ( t, x, y );
8561
8562 return TRUE;
8563 }
8564 return FALSE;
8565}
8566
8567static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8568{
8569 if ( t->holding && event->button == 1 )
8570 {
8571 VikCoord new_coord;
8572 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8573
8574 // snap to TP
8575 if ( event->state & GDK_CONTROL_MASK )
8576 {
8577 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8578 if ( tp )
8579 new_coord = tp->coord;
8580 }
8581
8582 // snap to WP
8583 if ( event->state & GDK_SHIFT_MASK )
8584 {
8585 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8586 if ( wp )
8587 new_coord = wp->coord;
8588 }
8589
8590 marker_end_move ( t );
8591
8592 // Determine if working on a waypoint or a trackpoint
8593 if ( t->is_waypoint ) {
8594 // Update waypoint position
8595 vtl->current_wp->coord = new_coord;
8596 trw_layer_calculate_bounds_waypoints ( vtl );
8597 // Reset waypoint pointer
8598 vtl->current_wp = NULL;
8599 vtl->current_wp_id = NULL;
8600 }
8601 else {
8602 if ( vtl->current_tpl ) {
8603 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8604
8605 if ( vtl->current_tp_track )
8606 vik_track_calculate_bounds ( vtl->current_tp_track );
8607
8608 if ( vtl->tpwin )
8609 if ( vtl->current_tp_track )
8610 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8611 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8612 }
8613 }
8614
8615 vik_layer_emit_update ( VIK_LAYER(vtl) );
8616 return TRUE;
8617 }
8618 return FALSE;
8619}
8620
8621/*
8622 Returns true if a waypoint or track is found near the requested event position for this particular layer
8623 The item found is automatically selected
8624 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8625 */
8626static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8627{
8628 if ( event->button != 1 )
8629 return FALSE;
8630
8631 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8632 return FALSE;
8633
8634 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8635 return FALSE;
8636
8637 LatLonBBox bbox;
8638 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8639
8640 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8641
8642 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8643 WPSearchParams wp_params;
8644 wp_params.vvp = vvp;
8645 wp_params.x = event->x;
8646 wp_params.y = event->y;
8647 wp_params.draw_images = vtl->drawimages;
8648 wp_params.closest_wp_id = NULL;
8649 wp_params.closest_wp = NULL;
8650
8651 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8652
8653 if ( wp_params.closest_wp ) {
8654
8655 // Select
8656 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8657
8658 // Too easy to move it so must be holding shift to start immediately moving it
8659 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8660 if ( event->state & GDK_SHIFT_MASK ||
8661 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8662 // Put into 'move buffer'
8663 // NB vvp & vw already set in tet
8664 tet->vtl = (gpointer)vtl;
8665 tet->is_waypoint = TRUE;
8666
8667 marker_begin_move (tet, event->x, event->y);
8668 }
8669
8670 vtl->current_wp = wp_params.closest_wp;
8671 vtl->current_wp_id = wp_params.closest_wp_id;
8672
8673 vik_layer_emit_update ( VIK_LAYER(vtl) );
8674
8675 return TRUE;
8676 }
8677 }
8678
8679 // Used for both track and route lists
8680 TPSearchParams tp_params;
8681 tp_params.vvp = vvp;
8682 tp_params.x = event->x;
8683 tp_params.y = event->y;
8684 tp_params.closest_track_id = NULL;
8685 tp_params.closest_tp = NULL;
8686 tp_params.closest_tpl = NULL;
8687 tp_params.bbox = bbox;
8688
8689 if (vtl->tracks_visible) {
8690 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8691
8692 if ( tp_params.closest_tp ) {
8693
8694 // Always select + highlight the track
8695 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8696
8697 tet->is_waypoint = FALSE;
8698
8699 // Select the Trackpoint
8700 // Can move it immediately when control held or it's the previously selected tp
8701 if ( event->state & GDK_CONTROL_MASK ||
8702 vtl->current_tpl == tp_params.closest_tpl ) {
8703 // Put into 'move buffer'
8704 // NB vvp & vw already set in tet
8705 tet->vtl = (gpointer)vtl;
8706 marker_begin_move (tet, event->x, event->y);
8707 }
8708
8709 vtl->current_tpl = tp_params.closest_tpl;
8710 vtl->current_tp_id = tp_params.closest_track_id;
8711 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8712
8713 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8714
8715 if ( vtl->tpwin )
8716 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8717
8718 vik_layer_emit_update ( VIK_LAYER(vtl) );
8719 return TRUE;
8720 }
8721 }
8722
8723 // Try again for routes
8724 if (vtl->routes_visible) {
8725 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8726
8727 if ( tp_params.closest_tp ) {
8728
8729 // Always select + highlight the track
8730 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8731
8732 tet->is_waypoint = FALSE;
8733
8734 // Select the Trackpoint
8735 // Can move it immediately when control held or it's the previously selected tp
8736 if ( event->state & GDK_CONTROL_MASK ||
8737 vtl->current_tpl == tp_params.closest_tpl ) {
8738 // Put into 'move buffer'
8739 // NB vvp & vw already set in tet
8740 tet->vtl = (gpointer)vtl;
8741 marker_begin_move (tet, event->x, event->y);
8742 }
8743
8744 vtl->current_tpl = tp_params.closest_tpl;
8745 vtl->current_tp_id = tp_params.closest_track_id;
8746 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8747
8748 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8749
8750 if ( vtl->tpwin )
8751 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8752
8753 vik_layer_emit_update ( VIK_LAYER(vtl) );
8754 return TRUE;
8755 }
8756 }
8757
8758 /* these aren't the droids you're looking for */
8759 vtl->current_wp = NULL;
8760 vtl->current_wp_id = NULL;
8761 trw_layer_cancel_current_tp ( vtl, FALSE );
8762
8763 // Blank info
8764 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8765
8766 return FALSE;
8767}
8768
8769static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8770{
8771 if ( event->button != 3 )
8772 return FALSE;
8773
8774 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8775 return FALSE;
8776
8777 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8778 return FALSE;
8779
8780 /* Post menu for the currently selected item */
8781
8782 /* See if a track is selected */
8783 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8784 if ( track && track->visible ) {
8785
8786 if ( track->name ) {
8787
8788 if ( vtl->track_right_click_menu )
8789 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8790
8791 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8792
8793 trku_udata udataU;
8794 udataU.trk = track;
8795 udataU.uuid = NULL;
8796
8797 gpointer *trkf;
8798 if ( track->is_route )
8799 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8800 else
8801 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8802
8803 if ( trkf && udataU.uuid ) {
8804
8805 GtkTreeIter *iter;
8806 if ( track->is_route )
8807 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8808 else
8809 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8810
8811 trw_layer_sublayer_add_menu_items ( vtl,
8812 vtl->track_right_click_menu,
8813 NULL,
8814 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8815 udataU.uuid,
8816 iter,
8817 vvp );
8818 }
8819
8820 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8821
8822 return TRUE;
8823 }
8824 }
8825
8826 /* See if a waypoint is selected */
8827 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8828 if ( waypoint && waypoint->visible ) {
8829 if ( waypoint->name ) {
8830
8831 if ( vtl->wp_right_click_menu )
8832 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8833
8834 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8835
8836 wpu_udata udata;
8837 udata.wp = waypoint;
8838 udata.uuid = NULL;
8839
8840 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8841
8842 if ( wpf && udata.uuid ) {
8843 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8844
8845 trw_layer_sublayer_add_menu_items ( vtl,
8846 vtl->wp_right_click_menu,
8847 NULL,
8848 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8849 udata.uuid,
8850 iter,
8851 vvp );
8852 }
8853 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8854
8855 return TRUE;
8856 }
8857 }
8858
8859 return FALSE;
8860}
8861
8862/* background drawing hook, to be passed the viewport */
8863static gboolean tool_sync_done = TRUE;
8864
8865static gboolean tool_sync(gpointer data)
8866{
8867 VikViewport *vvp = data;
8868 gdk_threads_enter();
8869 vik_viewport_sync(vvp);
8870 tool_sync_done = TRUE;
8871 gdk_threads_leave();
8872 return FALSE;
8873}
8874
8875static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8876{
8877 t->holding = TRUE;
8878 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8879 gdk_gc_set_function ( t->gc, GDK_INVERT );
8880 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8881 vik_viewport_sync(t->vvp);
8882 t->oldx = x;
8883 t->oldy = y;
8884}
8885
8886static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8887{
8888 VikViewport *vvp = t->vvp;
8889 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8890 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8891 t->oldx = x;
8892 t->oldy = y;
8893
8894 if (tool_sync_done) {
8895 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8896 tool_sync_done = FALSE;
8897 }
8898}
8899
8900static void marker_end_move ( tool_ed_t *t )
8901{
8902 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8903 g_object_unref ( t->gc );
8904 t->holding = FALSE;
8905}
8906
8907/*** Edit waypoint ****/
8908
8909static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8910{
8911 tool_ed_t *t = g_new(tool_ed_t, 1);
8912 t->vvp = vvp;
8913 t->holding = FALSE;
8914 return t;
8915}
8916
8917static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8918{
8919 g_free ( t );
8920}
8921
8922static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8923{
8924 WPSearchParams params;
8925 tool_ed_t *t = data;
8926 VikViewport *vvp = t->vvp;
8927
8928 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8929 return FALSE;
8930
8931 if ( t->holding ) {
8932 return TRUE;
8933 }
8934
8935 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8936 return FALSE;
8937
8938 if ( vtl->current_wp && vtl->current_wp->visible )
8939 {
8940 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8941 gint x, y;
8942 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8943
8944 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8945 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8946 {
8947 if ( event->button == 3 )
8948 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8949 else {
8950 marker_begin_move(t, event->x, event->y);
8951 }
8952 return TRUE;
8953 }
8954 }
8955
8956 params.vvp = vvp;
8957 params.x = event->x;
8958 params.y = event->y;
8959 params.draw_images = vtl->drawimages;
8960 params.closest_wp_id = NULL;
8961 params.closest_wp = NULL;
8962 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
8963 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
8964 {
8965 marker_begin_move(t, event->x, event->y);
8966 return FALSE;
8967 }
8968 else if ( params.closest_wp )
8969 {
8970 if ( event->button == 3 )
8971 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8972 else
8973 vtl->waypoint_rightclick = FALSE;
8974
8975 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8976
8977 vtl->current_wp = params.closest_wp;
8978 vtl->current_wp_id = params.closest_wp_id;
8979
8980 /* could make it so don't update if old WP is off screen and new is null but oh well */
8981 vik_layer_emit_update ( VIK_LAYER(vtl) );
8982 return TRUE;
8983 }
8984
8985 vtl->current_wp = NULL;
8986 vtl->current_wp_id = NULL;
8987 vtl->waypoint_rightclick = FALSE;
8988 vik_layer_emit_update ( VIK_LAYER(vtl) );
8989 return FALSE;
8990}
8991
8992static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8993{
8994 tool_ed_t *t = data;
8995 VikViewport *vvp = t->vvp;
8996
8997 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8998 return FALSE;
8999
9000 if ( t->holding ) {
9001 VikCoord new_coord;
9002 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9003
9004 /* snap to TP */
9005 if ( event->state & GDK_CONTROL_MASK )
9006 {
9007 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9008 if ( tp )
9009 new_coord = tp->coord;
9010 }
9011
9012 /* snap to WP */
9013 if ( event->state & GDK_SHIFT_MASK )
9014 {
9015 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9016 if ( wp && wp != vtl->current_wp )
9017 new_coord = wp->coord;
9018 }
9019
9020 {
9021 gint x, y;
9022 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9023
9024 marker_moveto ( t, x, y );
9025 }
9026 return TRUE;
9027 }
9028 return FALSE;
9029}
9030
9031static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9032{
9033 tool_ed_t *t = data;
9034 VikViewport *vvp = t->vvp;
9035
9036 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9037 return FALSE;
9038
9039 if ( t->holding && event->button == 1 )
9040 {
9041 VikCoord new_coord;
9042 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9043
9044 /* snap to TP */
9045 if ( event->state & GDK_CONTROL_MASK )
9046 {
9047 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9048 if ( tp )
9049 new_coord = tp->coord;
9050 }
9051
9052 /* snap to WP */
9053 if ( event->state & GDK_SHIFT_MASK )
9054 {
9055 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9056 if ( wp && wp != vtl->current_wp )
9057 new_coord = wp->coord;
9058 }
9059
9060 marker_end_move ( t );
9061
9062 vtl->current_wp->coord = new_coord;
9063
9064 trw_layer_calculate_bounds_waypoints ( vtl );
9065 vik_layer_emit_update ( VIK_LAYER(vtl) );
9066 return TRUE;
9067 }
9068 /* PUT IN RIGHT PLACE!!! */
9069 if ( event->button == 3 && vtl->waypoint_rightclick )
9070 {
9071 if ( vtl->wp_right_click_menu )
9072 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9073 if ( vtl->current_wp ) {
9074 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9075 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 );
9076 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9077 }
9078 vtl->waypoint_rightclick = FALSE;
9079 }
9080 return FALSE;
9081}
9082
9083/*** New track ****/
9084
9085static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9086{
9087 return vvp;
9088}
9089
9090typedef struct {
9091 VikTrwLayer *vtl;
9092 GdkDrawable *drawable;
9093 GdkGC *gc;
9094 GdkPixmap *pixmap;
9095} draw_sync_t;
9096
9097/*
9098 * Draw specified pixmap
9099 */
9100static gboolean draw_sync ( gpointer data )
9101{
9102 draw_sync_t *ds = (draw_sync_t*) data;
9103 // Sometimes don't want to draw
9104 // normally because another update has taken precedent such as panning the display
9105 // which means this pixmap is no longer valid
9106 if ( ds->vtl->draw_sync_do ) {
9107 gdk_threads_enter();
9108 gdk_draw_drawable (ds->drawable,
9109 ds->gc,
9110 ds->pixmap,
9111 0, 0, 0, 0, -1, -1);
9112 ds->vtl->draw_sync_done = TRUE;
9113 gdk_threads_leave();
9114 }
9115 g_free ( ds );
9116 return FALSE;
9117}
9118
9119static gchar* distance_string (gdouble distance)
9120{
9121 gchar str[128];
9122
9123 /* draw label with distance */
9124 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9125 switch (dist_units) {
9126 case VIK_UNITS_DISTANCE_MILES:
9127 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9128 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9129 } else if (distance < 1609.4) {
9130 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9131 } else {
9132 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9133 }
9134 break;
9135 default:
9136 // VIK_UNITS_DISTANCE_KILOMETRES
9137 if (distance >= 1000 && distance < 100000) {
9138 g_sprintf(str, "%3.2f km", distance/1000.0);
9139 } else if (distance < 1000) {
9140 g_sprintf(str, "%d m", (int)distance);
9141 } else {
9142 g_sprintf(str, "%d km", (int)distance/1000);
9143 }
9144 break;
9145 }
9146 return g_strdup (str);
9147}
9148
9149/*
9150 * Actually set the message in statusbar
9151 */
9152static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9153{
9154 // Only show elevation data when track has some elevation properties
9155 gchar str_gain_loss[64];
9156 str_gain_loss[0] = '\0';
9157 gchar str_last_step[64];
9158 str_last_step[0] = '\0';
9159 gchar *str_total = distance_string (distance);
9160
9161 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9162 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9163 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9164 else
9165 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9166 }
9167
9168 if ( last_step > 0 ) {
9169 gchar *tmp = distance_string (last_step);
9170 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9171 g_free ( tmp );
9172 }
9173
9174 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9175
9176 // Write with full gain/loss information
9177 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9178 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9179 g_free ( msg );
9180 g_free ( str_total );
9181}
9182
9183/*
9184 * Figure out what information should be set in the statusbar and then write it
9185 */
9186static void update_statusbar ( VikTrwLayer *vtl )
9187{
9188 // Get elevation data
9189 gdouble elev_gain, elev_loss;
9190 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9191
9192 /* Find out actual distance of current track */
9193 gdouble distance = vik_track_get_length (vtl->current_track);
9194
9195 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9196}
9197
9198
9199static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9200{
9201 /* if we haven't sync'ed yet, we don't have time to do more. */
9202 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9203 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9204
9205 static GdkPixmap *pixmap = NULL;
9206 int w1, h1, w2, h2;
9207 // Need to check in case window has been resized
9208 w1 = vik_viewport_get_width(vvp);
9209 h1 = vik_viewport_get_height(vvp);
9210 if (!pixmap) {
9211 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9212 }
9213 gdk_drawable_get_size (pixmap, &w2, &h2);
9214 if (w1 != w2 || h1 != h2) {
9215 g_object_unref ( G_OBJECT ( pixmap ) );
9216 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9217 }
9218
9219 // Reset to background
9220 gdk_draw_drawable (pixmap,
9221 vtl->current_track_newpoint_gc,
9222 vik_viewport_get_pixmap(vvp),
9223 0, 0, 0, 0, -1, -1);
9224
9225 draw_sync_t *passalong;
9226 gint x1, y1;
9227
9228 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9229
9230 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9231 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9232 // thus when we come to reset to the background it would include what we have already drawn!!
9233 gdk_draw_line ( pixmap,
9234 vtl->current_track_newpoint_gc,
9235 x1, y1, event->x, event->y );
9236 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9237
9238 /* Find out actual distance of current track */
9239 gdouble distance = vik_track_get_length (vtl->current_track);
9240
9241 // Now add distance to where the pointer is //
9242 VikCoord coord;
9243 struct LatLon ll;
9244 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9245 vik_coord_to_latlon ( &coord, &ll );
9246 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9247 distance = distance + last_step;
9248
9249 // Get elevation data
9250 gdouble elev_gain, elev_loss;
9251 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9252
9253 // Adjust elevation data (if available) for the current pointer position
9254 gdouble elev_new;
9255 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9256 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9257 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9258 // Adjust elevation of last track point
9259 if ( elev_new > last_tpt->altitude )
9260 // Going up
9261 elev_gain += elev_new - last_tpt->altitude;
9262 else
9263 // Going down
9264 elev_loss += last_tpt->altitude - elev_new;
9265 }
9266 }
9267
9268 //
9269 // Display of the distance 'tooltip' during track creation is controlled by a preference
9270 //
9271 if ( a_vik_get_create_track_tooltip() ) {
9272
9273 gchar *str = distance_string (distance);
9274
9275 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9276 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9277 pango_layout_set_text (pl, str, -1);
9278 gint wd, hd;
9279 pango_layout_get_pixel_size ( pl, &wd, &hd );
9280
9281 gint xd,yd;
9282 // offset from cursor a bit depending on font size
9283 xd = event->x + 10;
9284 yd = event->y - hd;
9285
9286 // Create a background block to make the text easier to read over the background map
9287 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9288 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9289 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9290
9291 g_object_unref ( G_OBJECT ( pl ) );
9292 g_object_unref ( G_OBJECT ( background_block_gc ) );
9293 g_free (str);
9294 }
9295
9296 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9297 passalong->vtl = vtl;
9298 passalong->pixmap = pixmap;
9299 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9300 passalong->gc = vtl->current_track_newpoint_gc;
9301
9302 gdouble angle;
9303 gdouble baseangle;
9304 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9305
9306 // Update statusbar with full gain/loss information
9307 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9308
9309 // draw pixmap when we have time to
9310 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9311 vtl->draw_sync_done = FALSE;
9312 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9313 }
9314 return VIK_LAYER_TOOL_ACK;
9315}
9316
9317// NB vtl->current_track must be valid
9318static void undo_trackpoint_add ( VikTrwLayer *vtl )
9319{
9320 // 'undo'
9321 if ( vtl->current_track->trackpoints ) {
9322 // TODO rework this...
9323 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9324 GList *last = g_list_last(vtl->current_track->trackpoints);
9325 g_free ( last->data );
9326 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9327
9328 vik_track_calculate_bounds ( vtl->current_track );
9329 }
9330}
9331
9332static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9333{
9334 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9335 vtl->current_track = NULL;
9336 vik_layer_emit_update ( VIK_LAYER(vtl) );
9337 return TRUE;
9338 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9339 undo_trackpoint_add ( vtl );
9340 update_statusbar ( vtl );
9341 vik_layer_emit_update ( VIK_LAYER(vtl) );
9342 return TRUE;
9343 }
9344 return FALSE;
9345}
9346
9347/*
9348 * Common function to handle trackpoint button requests on either a route or a track
9349 * . enables adding a point via normal click
9350 * . enables removal of last point via right click
9351 * . finishing of the track or route via double clicking
9352 */
9353static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9354{
9355 VikTrackpoint *tp;
9356
9357 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9358 return FALSE;
9359
9360 if ( event->button == 2 ) {
9361 // As the display is panning, the new track pixmap is now invalid so don't draw it
9362 // otherwise this drawing done results in flickering back to an old image
9363 vtl->draw_sync_do = FALSE;
9364 return FALSE;
9365 }
9366
9367 if ( event->button == 3 )
9368 {
9369 if ( !vtl->current_track )
9370 return FALSE;
9371 undo_trackpoint_add ( vtl );
9372 update_statusbar ( vtl );
9373 vik_layer_emit_update ( VIK_LAYER(vtl) );
9374 return TRUE;
9375 }
9376
9377 if ( event->type == GDK_2BUTTON_PRESS )
9378 {
9379 /* subtract last (duplicate from double click) tp then end */
9380 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9381 {
9382 /* undo last, then end */
9383 undo_trackpoint_add ( vtl );
9384 vtl->current_track = NULL;
9385 }
9386 vik_layer_emit_update ( VIK_LAYER(vtl) );
9387 return TRUE;
9388 }
9389
9390 tp = vik_trackpoint_new();
9391 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9392
9393 /* snap to other TP */
9394 if ( event->state & GDK_CONTROL_MASK )
9395 {
9396 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9397 if ( other_tp )
9398 tp->coord = other_tp->coord;
9399 }
9400
9401 tp->newsegment = FALSE;
9402 tp->has_timestamp = FALSE;
9403 tp->timestamp = 0;
9404
9405 if ( vtl->current_track ) {
9406 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9407 /* Auto attempt to get elevation from DEM data (if it's available) */
9408 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9409 }
9410
9411 vtl->ct_x1 = vtl->ct_x2;
9412 vtl->ct_y1 = vtl->ct_y2;
9413 vtl->ct_x2 = event->x;
9414 vtl->ct_y2 = event->y;
9415
9416 vik_layer_emit_update ( VIK_LAYER(vtl) );
9417 return TRUE;
9418}
9419
9420static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9421{
9422 // ----------------------------------------------------- if current is a route - switch to new track
9423 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9424 {
9425 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9426 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9427 {
9428 new_track_create_common ( vtl, name );
9429 g_free ( name );
9430 }
9431 else
9432 return TRUE;
9433 }
9434 return tool_new_track_or_route_click ( vtl, event, vvp );
9435}
9436
9437static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9438{
9439 if ( event->button == 2 ) {
9440 // Pan moving ended - enable potential point drawing again
9441 vtl->draw_sync_do = TRUE;
9442 vtl->draw_sync_done = TRUE;
9443 }
9444}
9445
9446/*** New route ****/
9447
9448static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9449{
9450 return vvp;
9451}
9452
9453static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9454{
9455 // -------------------------- if current is a track - switch to new route
9456 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9457 {
9458 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9459 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9460 new_route_create_common ( vtl, name );
9461 g_free ( name );
9462 }
9463 else
9464 return TRUE;
9465 }
9466 return tool_new_track_or_route_click ( vtl, event, vvp );
9467}
9468
9469/*** New waypoint ****/
9470
9471static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9472{
9473 return vvp;
9474}
9475
9476static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9477{
9478 VikCoord coord;
9479 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9480 return FALSE;
9481 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9482 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9483 trw_layer_calculate_bounds_waypoints ( vtl );
9484 vik_layer_emit_update ( VIK_LAYER(vtl) );
9485 }
9486 return TRUE;
9487}
9488
9489
9490/*** Edit trackpoint ****/
9491
9492static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9493{
9494 tool_ed_t *t = g_new(tool_ed_t, 1);
9495 t->vvp = vvp;
9496 t->holding = FALSE;
9497 return t;
9498}
9499
9500static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9501{
9502 g_free ( t );
9503}
9504
9505static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9506{
9507 tool_ed_t *t = data;
9508 VikViewport *vvp = t->vvp;
9509 TPSearchParams params;
9510 /* OUTDATED DOCUMENTATION:
9511 find 5 pixel range on each side. then put these UTM, and a pointer
9512 to the winning track name (and maybe the winning track itself), and a
9513 pointer to the winning trackpoint, inside an array or struct. pass
9514 this along, do a foreach on the tracks which will do a foreach on the
9515 trackpoints. */
9516 params.vvp = vvp;
9517 params.x = event->x;
9518 params.y = event->y;
9519 params.closest_track_id = NULL;
9520 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9521 params.closest_tp = NULL;
9522 params.closest_tpl = NULL;
9523 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9524
9525 if ( event->button != 1 )
9526 return FALSE;
9527
9528 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9529 return FALSE;
9530
9531 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9532 return FALSE;
9533
9534 if ( vtl->current_tpl )
9535 {
9536 /* 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.) */
9537 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9538 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9539 if ( !current_tr )
9540 return FALSE;
9541
9542 gint x, y;
9543 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9544
9545 if ( current_tr->visible &&
9546 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9547 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9548 marker_begin_move ( t, event->x, event->y );
9549 return TRUE;
9550 }
9551
9552 }
9553
9554 if ( vtl->tracks_visible )
9555 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
9556
9557 if ( params.closest_tp )
9558 {
9559 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9560 vtl->current_tpl = params.closest_tpl;
9561 vtl->current_tp_id = params.closest_track_id;
9562 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9563 trw_layer_tpwin_init ( vtl );
9564 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9565 vik_layer_emit_update ( VIK_LAYER(vtl) );
9566 return TRUE;
9567 }
9568
9569 if ( vtl->routes_visible )
9570 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
9571
9572 if ( params.closest_tp )
9573 {
9574 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9575 vtl->current_tpl = params.closest_tpl;
9576 vtl->current_tp_id = params.closest_track_id;
9577 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9578 trw_layer_tpwin_init ( vtl );
9579 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9580 vik_layer_emit_update ( VIK_LAYER(vtl) );
9581 return TRUE;
9582 }
9583
9584 /* these aren't the droids you're looking for */
9585 return FALSE;
9586}
9587
9588static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9589{
9590 tool_ed_t *t = data;
9591 VikViewport *vvp = t->vvp;
9592
9593 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9594 return FALSE;
9595
9596 if ( t->holding )
9597 {
9598 VikCoord new_coord;
9599 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9600
9601 /* snap to TP */
9602 if ( event->state & GDK_CONTROL_MASK )
9603 {
9604 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9605 if ( tp && tp != vtl->current_tpl->data )
9606 new_coord = tp->coord;
9607 }
9608 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9609 {
9610 gint x, y;
9611 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9612 marker_moveto ( t, x, y );
9613 }
9614
9615 return TRUE;
9616 }
9617 return FALSE;
9618}
9619
9620static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9621{
9622 tool_ed_t *t = data;
9623 VikViewport *vvp = t->vvp;
9624
9625 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9626 return FALSE;
9627 if ( event->button != 1)
9628 return FALSE;
9629
9630 if ( t->holding ) {
9631 VikCoord new_coord;
9632 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9633
9634 /* snap to TP */
9635 if ( event->state & GDK_CONTROL_MASK )
9636 {
9637 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9638 if ( tp && tp != vtl->current_tpl->data )
9639 new_coord = tp->coord;
9640 }
9641
9642 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9643 if ( vtl->current_tp_track )
9644 vik_track_calculate_bounds ( vtl->current_tp_track );
9645
9646 marker_end_move ( t );
9647
9648 /* diff dist is diff from orig */
9649 if ( vtl->tpwin )
9650 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9651
9652 vik_layer_emit_update ( VIK_LAYER(vtl) );
9653 return TRUE;
9654 }
9655 return FALSE;
9656}
9657
9658
9659/*** Route Finder ***/
9660static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9661{
9662 return vvp;
9663}
9664
9665static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9666{
9667 VikCoord tmp;
9668 if ( !vtl ) return FALSE;
9669 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9670 if ( event->button == 3 && vtl->route_finder_current_track ) {
9671 VikCoord *new_end;
9672 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9673 if ( new_end ) {
9674 vtl->route_finder_coord = *new_end;
9675 g_free ( new_end );
9676 vik_layer_emit_update ( VIK_LAYER(vtl) );
9677 /* remove last ' to:...' */
9678 if ( vtl->route_finder_current_track->comment ) {
9679 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9680 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9681 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9682 last_to - vtl->route_finder_current_track->comment - 1);
9683 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9684 }
9685 }
9686 }
9687 }
9688 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9689 struct LatLon start, end;
9690
9691 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9692 vik_coord_to_latlon ( &(tmp), &end );
9693 vtl->route_finder_coord = tmp; /* for continuations */
9694
9695 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9696 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9697 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9698 } else {
9699 vtl->route_finder_check_added_track = TRUE;
9700 vtl->route_finder_started = FALSE;
9701 }
9702
9703 vik_routing_default_find ( vtl, start, end);
9704
9705 /* see if anything was done -- a track was added or appended to */
9706 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9707 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 ) );
9708 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9709 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9710 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9711 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9712 }
9713
9714 if ( vtl->route_finder_added_track )
9715 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9716
9717 vtl->route_finder_added_track = NULL;
9718 vtl->route_finder_check_added_track = FALSE;
9719 vtl->route_finder_append = FALSE;
9720
9721 vik_layer_emit_update ( VIK_LAYER(vtl) );
9722 } else {
9723 vtl->route_finder_started = TRUE;
9724 vtl->route_finder_coord = tmp;
9725 vtl->route_finder_current_track = NULL;
9726 }
9727 return TRUE;
9728}
9729
9730/*** Show picture ****/
9731
9732static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9733{
9734 return vvp;
9735}
9736
9737/* Params are: vvp, event, last match found or NULL */
9738static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9739{
9740 if ( wp->image && wp->visible )
9741 {
9742 gint x, y, slackx, slacky;
9743 GdkEventButton *event = (GdkEventButton *) params[1];
9744
9745 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9746 slackx = wp->image_width / 2;
9747 slacky = wp->image_height / 2;
9748 if ( x <= event->x + slackx && x >= event->x - slackx
9749 && y <= event->y + slacky && y >= event->y - slacky )
9750 {
9751 params[2] = wp->image; /* we've found a match. however continue searching
9752 * since we want to find the last match -- that
9753 * is, the match that was drawn last. */
9754 }
9755 }
9756}
9757
9758static void trw_layer_show_picture ( menu_array_sublayer values )
9759{
9760 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9761#ifdef WINDOWS
9762 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
9763#else /* WINDOWS */
9764 GError *err = NULL;
9765 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
9766 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9767 g_free ( quoted_file );
9768 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9769 {
9770 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(values[MA_VTL]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
9771 g_error_free ( err );
9772 }
9773 g_free ( cmd );
9774#endif /* WINDOWS */
9775}
9776
9777static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9778{
9779 gpointer params[3] = { vvp, event, NULL };
9780 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9781 return FALSE;
9782 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9783 if ( params[2] )
9784 {
9785 static menu_array_sublayer values;
9786 values[MA_VTL] = vtl;
9787 values[MA_MISC] = params[2];
9788 trw_layer_show_picture ( values );
9789 return TRUE; /* found a match */
9790 }
9791 else
9792 return FALSE; /* go through other layers, searching for a match */
9793}
9794
9795/***************************************************************************
9796 ** End tool code
9797 ***************************************************************************/
9798
9799
9800static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9801{
9802 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9803 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9804}
9805
9806/* Structure for thumbnail creating data used in the background thread */
9807typedef struct {
9808 VikTrwLayer *vtl; // Layer needed for redrawing
9809 GSList *pics; // Image list
9810} thumbnail_create_thread_data;
9811
9812static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9813{
9814 guint total = g_slist_length(tctd->pics), done = 0;
9815 while ( tctd->pics )
9816 {
9817 a_thumbnails_create ( (gchar *) tctd->pics->data );
9818 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9819 if ( result != 0 )
9820 return -1; /* Abort thread */
9821
9822 tctd->pics = tctd->pics->next;
9823 }
9824
9825 // Redraw to show the thumbnails as they are now created
9826 if ( IS_VIK_LAYER(tctd->vtl) )
9827 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9828
9829 return 0;
9830}
9831
9832static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9833{
9834 while ( tctd->pics )
9835 {
9836 g_free ( tctd->pics->data );
9837 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9838 }
9839 g_free ( tctd );
9840}
9841
9842void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9843{
9844 if ( ! vtl->has_verified_thumbnails )
9845 {
9846 GSList *pics = NULL;
9847 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9848 if ( pics )
9849 {
9850 gint len = g_slist_length ( pics );
9851 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9852 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9853 tctd->vtl = vtl;
9854 tctd->pics = pics;
9855 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9856 tmp,
9857 (vik_thr_func) create_thumbnails_thread,
9858 tctd,
9859 (vik_thr_free_func) thumbnail_create_thread_free,
9860 NULL,
9861 len );
9862 g_free ( tmp );
9863 }
9864 }
9865}
9866
9867static const gchar* my_track_colors ( gint ii )
9868{
9869 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9870 "#2d870a",
9871 "#135D34",
9872 "#0a8783",
9873 "#0e4d87",
9874 "#05469f",
9875 "#695CBB",
9876 "#2d059f",
9877 "#4a059f",
9878 "#5A171A",
9879 "#96059f"
9880 };
9881 // Fast and reliable way of returning a colour
9882 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9883}
9884
9885static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9886{
9887 GHashTableIter iter;
9888 gpointer key, value;
9889
9890 gint ii = 0;
9891 // Tracks
9892 g_hash_table_iter_init ( &iter, vtl->tracks );
9893
9894 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9895
9896 // Tracks get a random spread of colours if not already assigned
9897 if ( ! VIK_TRACK(value)->has_color ) {
9898 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9899 VIK_TRACK(value)->color = vtl->track_color;
9900 else {
9901 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9902 }
9903 VIK_TRACK(value)->has_color = TRUE;
9904 }
9905
9906 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9907
9908 ii++;
9909 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9910 ii = 0;
9911 }
9912
9913 // Routes
9914 ii = 0;
9915 g_hash_table_iter_init ( &iter, vtl->routes );
9916
9917 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9918
9919 // Routes get an intermix of reds
9920 if ( ! VIK_TRACK(value)->has_color ) {
9921 if ( ii )
9922 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9923 else
9924 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9925 VIK_TRACK(value)->has_color = TRUE;
9926 }
9927
9928 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9929
9930 ii = !ii;
9931 }
9932}
9933
9934/*
9935 * (Re)Calculate the bounds of the waypoints in this layer,
9936 * This should be called whenever waypoints are changed
9937 */
9938void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9939{
9940 struct LatLon topleft = { 0.0, 0.0 };
9941 struct LatLon bottomright = { 0.0, 0.0 };
9942 struct LatLon ll;
9943
9944 GHashTableIter iter;
9945 gpointer key, value;
9946
9947 g_hash_table_iter_init ( &iter, vtl->waypoints );
9948
9949 // Set bounds to first point
9950 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9951 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9952 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9953 }
9954
9955 // Ensure there is another point...
9956 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9957
9958 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9959
9960 // See if this point increases the bounds.
9961 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9962
9963 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9964 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9965 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9966 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9967 }
9968 }
9969
9970 vtl->waypoints_bbox.north = topleft.lat;
9971 vtl->waypoints_bbox.east = bottomright.lon;
9972 vtl->waypoints_bbox.south = bottomright.lat;
9973 vtl->waypoints_bbox.west = topleft.lon;
9974}
9975
9976static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9977{
9978 vik_track_calculate_bounds ( trk );
9979}
9980
9981static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9982{
9983 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9984 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9985}
9986
9987static void trw_layer_sort_all ( VikTrwLayer *vtl )
9988{
9989 if ( ! VIK_LAYER(vtl)->vt )
9990 return;
9991
9992 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9993 if ( g_hash_table_size (vtl->tracks) > 1 )
9994 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9995
9996 if ( g_hash_table_size (vtl->routes) > 1 )
9997 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9998
9999 if ( g_hash_table_size (vtl->waypoints) > 1 )
10000 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10001}
10002
10003static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10004{
10005 if ( VIK_LAYER(vtl)->realized )
10006 trw_layer_verify_thumbnails ( vtl, vvp );
10007 trw_layer_track_alloc_colors ( vtl );
10008
10009 trw_layer_calculate_bounds_waypoints ( vtl );
10010 trw_layer_calculate_bounds_tracks ( vtl );
10011
10012 // Apply treeview sort after loading all the tracks for this layer
10013 // (rather than sorted insert on each individual track additional)
10014 // and after subsequent changes to the properties as the specified order may have changed.
10015 // since the sorting of a treeview section is now very quick
10016 // NB sorting is also performed after every name change as well to maintain the list order
10017 trw_layer_sort_all ( vtl );
10018}
10019
10020VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10021{
10022 return vtl->coord_mode;
10023}
10024
10025/**
10026 * Uniquify the whole layer
10027 * Also requires the layers panel as the names shown there need updating too
10028 * Returns whether the operation was successful or not
10029 */
10030gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10031{
10032 if ( vtl && vlp ) {
10033 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10034 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10035 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10036 return TRUE;
10037 }
10038 return FALSE;
10039}
10040
10041static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10042{
10043 vik_coord_convert ( &(wp->coord), *dest_mode );
10044}
10045
10046static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10047{
10048 vik_track_convert ( tr, *dest_mode );
10049}
10050
10051static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10052{
10053 if ( vtl->coord_mode != dest_mode )
10054 {
10055 vtl->coord_mode = dest_mode;
10056 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10057 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10058 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10059 }
10060}
10061
10062static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10063{
10064 vtl->menu_selection = selection;
10065}
10066
10067static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10068{
10069 return (vtl->menu_selection);
10070}
10071
10072/* ----------- Downloading maps along tracks --------------- */
10073
10074static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10075{
10076 /* TODO: calculating based on current size of viewport */
10077 const gdouble w_at_zoom_0_125 = 0.0013;
10078 const gdouble h_at_zoom_0_125 = 0.0011;
10079 gdouble zoom_factor = zoom_level/0.125;
10080
10081 wh->lat = h_at_zoom_0_125 * zoom_factor;
10082 wh->lon = w_at_zoom_0_125 * zoom_factor;
10083
10084 return 0; /* all OK */
10085}
10086
10087static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10088{
10089 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10090 (dist->lat >= ABS(to->north_south - from->north_south)))
10091 return NULL;
10092
10093 VikCoord *coord = g_malloc(sizeof(VikCoord));
10094 coord->mode = VIK_COORD_LATLON;
10095
10096 if (ABS(gradient) < 1) {
10097 if (from->east_west > to->east_west)
10098 coord->east_west = from->east_west - dist->lon;
10099 else
10100 coord->east_west = from->east_west + dist->lon;
10101 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10102 } else {
10103 if (from->north_south > to->north_south)
10104 coord->north_south = from->north_south - dist->lat;
10105 else
10106 coord->north_south = from->north_south + dist->lat;
10107 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10108 }
10109
10110 return coord;
10111}
10112
10113static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10114{
10115 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10116 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10117
10118 VikCoord *next = from;
10119 while (TRUE) {
10120 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10121 break;
10122 list = g_list_prepend(list, next);
10123 }
10124
10125 return list;
10126}
10127
10128void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10129{
10130 typedef struct _Rect {
10131 VikCoord tl;
10132 VikCoord br;
10133 VikCoord center;
10134 } Rect;
10135#define GLRECT(iter) ((Rect *)((iter)->data))
10136
10137 struct LatLon wh;
10138 GList *rects_to_download = NULL;
10139 GList *rect_iter;
10140
10141 if (get_download_area_width(vvp, zoom_level, &wh))
10142 return;
10143
10144 GList *iter = tr->trackpoints;
10145 if (!iter)
10146 return;
10147
10148 gboolean new_map = TRUE;
10149 VikCoord *cur_coord, tl, br;
10150 Rect *rect;
10151 while (iter) {
10152 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10153 if (new_map) {
10154 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10155 rect = g_malloc(sizeof(Rect));
10156 rect->tl = tl;
10157 rect->br = br;
10158 rect->center = *cur_coord;
10159 rects_to_download = g_list_prepend(rects_to_download, rect);
10160 new_map = FALSE;
10161 iter = iter->next;
10162 continue;
10163 }
10164 gboolean found = FALSE;
10165 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10166 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10167 found = TRUE;
10168 break;
10169 }
10170 }
10171 if (found)
10172 iter = iter->next;
10173 else
10174 new_map = TRUE;
10175 }
10176
10177 GList *fillins = NULL;
10178 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10179 /* seems that ATM the function get_next_coord works only for LATLON */
10180 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10181 /* fill-ins for far apart points */
10182 GList *cur_rect, *next_rect;
10183 for (cur_rect = rects_to_download;
10184 (next_rect = cur_rect->next) != NULL;
10185 cur_rect = cur_rect->next) {
10186 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10187 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10188 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10189 }
10190 }
10191 } else
10192 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10193
10194 if (fillins) {
10195 GList *iter = fillins;
10196 while (iter) {
10197 cur_coord = (VikCoord *)(iter->data);
10198 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10199 rect = g_malloc(sizeof(Rect));
10200 rect->tl = tl;
10201 rect->br = br;
10202 rect->center = *cur_coord;
10203 rects_to_download = g_list_prepend(rects_to_download, rect);
10204 iter = iter->next;
10205 }
10206 }
10207
10208 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10209 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10210 }
10211
10212 if (fillins) {
10213 for (iter = fillins; iter; iter = iter->next)
10214 g_free(iter->data);
10215 g_list_free(fillins);
10216 }
10217 if (rects_to_download) {
10218 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10219 g_free(rect_iter->data);
10220 g_list_free(rects_to_download);
10221 }
10222}
10223
10224static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10225{
10226 VikMapsLayer *vml;
10227 gint selected_map;
10228 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10229 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10230 gint selected_zoom, default_zoom;
10231
10232 VikTrwLayer *vtl = values[MA_VTL];
10233 VikLayersPanel *vlp = values[MA_VLP];
10234 VikTrack *trk;
10235 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10236 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10237 else
10238 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10239 if ( !trk )
10240 return;
10241
10242 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10243
10244 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10245 int num_maps = g_list_length(vmls);
10246
10247 if (!num_maps) {
10248 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10249 return;
10250 }
10251
10252 // Convert from list of vmls to list of names. Allowing the user to select one of them
10253 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10254 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10255
10256 gchar **np = map_names;
10257 VikMapsLayer **lp = map_layers;
10258 int i;
10259 for (i = 0; i < num_maps; i++) {
10260 vml = (VikMapsLayer *)(vmls->data);
10261 *lp++ = vml;
10262 *np++ = vik_maps_layer_get_map_label(vml);
10263 vmls = vmls->next;
10264 }
10265 // Mark end of the array lists
10266 *lp = NULL;
10267 *np = NULL;
10268
10269 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10270 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10271 if (cur_zoom == zoom_vals[default_zoom])
10272 break;
10273 }
10274 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10275
10276 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10277 goto done;
10278
10279 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10280
10281done:
10282 for (i = 0; i < num_maps; i++)
10283 g_free(map_names[i]);
10284 g_free(map_names);
10285 g_free(map_layers);
10286
10287 g_list_free(vmls);
10288
10289}
10290
10291/**** lowest waypoint number calculation ***/
10292static gint highest_wp_number_name_to_number(const gchar *name) {
10293 if ( strlen(name) == 3 ) {
10294 int n = atoi(name);
10295 if ( n < 100 && name[0] != '0' )
10296 return -1;
10297 if ( n < 10 && name[0] != '0' )
10298 return -1;
10299 return n;
10300 }
10301 return -1;
10302}
10303
10304
10305static void highest_wp_number_reset(VikTrwLayer *vtl)
10306{
10307 vtl->highest_wp_number = -1;
10308}
10309
10310static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10311{
10312 /* if is bigger that top, add it */
10313 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10314 if ( new_wp_num > vtl->highest_wp_number )
10315 vtl->highest_wp_number = new_wp_num;
10316}
10317
10318static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10319{
10320 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10321 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10322 if ( vtl->highest_wp_number == old_wp_num ) {
10323 gchar buf[4];
10324 vtl->highest_wp_number--;
10325
10326 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10327 /* search down until we find something that *does* exist */
10328
10329 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10330 vtl->highest_wp_number--;
10331 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10332 }
10333 }
10334}
10335
10336/* get lowest unused number */
10337static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10338{
10339 gchar buf[4];
10340 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10341 return NULL;
10342 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10343 return g_strdup(buf);
10344}
10345
10346/**
10347 * trw_layer_create_track_list_both:
10348 *
10349 * Create the latest list of tracks and routes
10350 */
10351static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10352{
10353 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10354 GList *tracks = NULL;
10355 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10356 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10357
10358 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10359}
10360
10361static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10362{
10363 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10364
10365 gchar *title = NULL;
10366 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10367 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10368 else
10369 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10370
10371 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10372 g_free ( title );
10373}
10374
10375static void trw_layer_track_list_dialog ( menu_array_layer values )
10376{
10377 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10378
10379 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10380 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10381 g_free ( title );
10382}
10383
10384static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10385{
10386 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10387
10388 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10389 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );
10390 g_free ( title );
10391}