2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2011, Rob Norris <rw_norris@hotmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #define WAYPOINT_FONT "Sans 8"
28 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
29 /* viktrwlayer.c -- 5000+ lines can make a difference in the state of things */
36 #include "vikmapslayer.h"
37 #include "viktrwlayer_tpwin.h"
38 #include "viktrwlayer_propwin.h"
39 #include "garminsymbols.h"
40 #include "thumbnails.h"
41 #include "background.h"
46 #include "geonamessearch.h"
47 #ifdef VIK_CONFIG_OPENSTREETMAP
48 #include "osm-traces.h"
51 #include "datasources.h"
54 #include "icons/icons.h"
68 #include <gdk/gdkkeysyms.h>
70 #include <glib/gstdio.h>
71 #include <glib/gi18n.h>
73 /* Relax some dependencies */
74 #if ! GLIB_CHECK_VERSION(2,12,0)
75 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
76 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
79 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
80 #define VIK_TRW_LAYER_TRACK_GC 13
81 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
82 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
83 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
84 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
86 #define DRAWMODE_BY_TRACK 0
87 #define DRAWMODE_BY_VELOCITY 1
88 #define DRAWMODE_ALL_BLACK 2
93 /* this is how it knows when you click if you are clicking close to a trackpoint. */
94 #define TRACKPOINT_SIZE_APPROX 5
95 #define WAYPOINT_SIZE_APPROX 5
97 #define MIN_STOP_LENGTH 15
98 #define MAX_STOP_LENGTH 86400
99 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
100 /* this is multiplied by user-inputted value from 1-100. */
102 VIK_TRW_LAYER_SUBLAYER_TRACKS,
103 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
104 VIK_TRW_LAYER_SUBLAYER_TRACK,
105 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
108 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
110 struct _VikTrwLayer {
113 GHashTable *tracks_iters;
114 GHashTable *waypoints_iters;
115 GHashTable *waypoints;
116 GtkTreeIter waypoints_iter, tracks_iter;
117 gboolean tracks_visible, waypoints_visible;
120 guint8 drawelevation;
121 guint8 elevation_factor;
125 guint8 line_thickness;
126 guint8 bg_line_thickness;
130 gboolean wp_draw_symbols;
132 gdouble velocity_min, velocity_max;
134 guint16 track_gc_iter;
135 GdkGC *current_track_gc;
138 GdkGC *waypoint_text_gc;
139 GdkGC *waypoint_bg_gc;
140 GdkFont *waypoint_font;
141 VikTrack *current_track;
142 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
143 gboolean ct_sync_done;
146 VikCoordMode coord_mode;
148 /* wp editing tool */
149 VikWaypoint *current_wp;
150 gchar *current_wp_name;
152 gboolean waypoint_rightclick;
154 /* track editing tool */
156 gchar *current_tp_track_name;
157 VikTrwLayerTpwin *tpwin;
159 /* weird hack for joining tracks */
161 gchar *last_tp_track_name;
163 /* track editing tool -- more specifically, moving tps */
166 /* route finder tool */
167 gboolean route_finder_started;
168 VikCoord route_finder_coord;
169 gboolean route_finder_check_added_track;
170 gchar *route_finder_added_track_name;
171 VikTrack *route_finder_current_track;
172 gboolean route_finder_append;
179 guint16 image_cache_size;
181 /* for waypoint text */
182 PangoLayout *wplabellayout;
184 gboolean has_verified_thumbnails;
186 GtkMenu *wp_right_click_menu;
187 GtkMenu *track_right_click_menu;
190 VikStdLayerMenuItem menu_selection;
192 gint highest_wp_number;
195 /* A caached waypoint image. */
198 gchar *image; /* filename */
201 struct DrawingParams {
205 guint16 width, height;
206 const VikCoord *center;
208 gboolean one_zone, lat_lon;
209 gdouble ce1, ce2, cn1, cn2;
212 static void trw_layer_delete_item ( gpointer pass_along[6] );
213 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
214 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
216 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
217 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
218 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
220 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
221 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
223 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
224 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
225 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
227 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
228 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
229 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
230 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
231 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
232 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
233 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
234 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
235 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
236 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
237 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
238 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
239 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
240 static void trw_layer_show_picture ( gpointer pass_along[6] );
242 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
243 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
244 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
245 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
246 static void trw_layer_new_wp ( gpointer lav[2] );
247 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
248 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
249 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
250 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
251 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
252 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
253 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
254 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
255 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
256 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
257 #ifdef VIK_CONFIG_OPENSTREETMAP
258 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
260 #ifdef VIK_CONFIG_GEOCACHES
261 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
265 static void trw_layer_properties_item ( gpointer pass_along[6] );
266 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
267 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
269 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
270 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
271 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
273 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
274 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
275 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
276 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
277 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
279 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
280 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
281 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
282 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
283 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
284 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
285 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
286 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
287 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
288 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
289 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
290 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
291 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
292 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
293 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
294 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
295 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
296 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
297 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
298 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
301 static void cached_pixbuf_free ( CachedPixbuf *cp );
302 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
304 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
305 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
307 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
308 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
309 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
311 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
312 static void highest_wp_number_reset(VikTrwLayer *vtl);
313 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
314 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
317 static VikToolInterface trw_layer_tools[] = {
318 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
319 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
321 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
322 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
323 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
325 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
326 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
328 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
329 (VikToolMouseFunc) tool_edit_waypoint_click,
330 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
331 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
333 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
334 (VikToolMouseFunc) tool_edit_trackpoint_click,
335 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
336 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
338 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
339 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
341 { N_("Route Finder"), (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
342 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
344 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
346 /****** PARAMETERS ******/
348 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
349 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
351 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
352 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
355 static VikLayerParamScale params_scales[] = {
356 /* min max step digits */
357 { 1, 10, 1, 0 }, /* line_thickness */
358 { 0.0, 99.0, 1, 2 }, /* velocity_min */
359 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
360 /* 5 * step == how much to turn */
361 { 16, 128, 3.2, 0 }, /* image_size */
362 { 0, 255, 5, 0 }, /* image alpha */
363 { 5, 500, 5, 0 }, /* image cache_size */
364 { 0, 8, 1, 0 }, /* image cache_size */
365 { 1, 64, 1, 0 }, /* wpsize */
366 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
367 { 1, 100, 1, 0 }, /* stop_length */
370 VikLayerParam trw_layer_params[] = {
371 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
372 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
374 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
375 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
376 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
377 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
378 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
380 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
381 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
383 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
384 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
385 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
386 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
387 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
389 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
390 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
391 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
392 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
393 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
394 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
395 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
396 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
398 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
399 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
400 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
401 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
404 enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_DE, PARAM_EF, PARAM_DS, PARAM_SL, PARAM_LT, PARAM_BLT, PARAM_TBGC, PARAM_VMIN, PARAM_VMAX, PARAM_DLA, PARAM_WPC, PARAM_WPTC, PARAM_WPBC, PARAM_WPBA, PARAM_WPSYM, PARAM_WPSIZE, PARAM_WPSYMS, PARAM_DI, PARAM_IS, PARAM_IA, PARAM_ICS, NUM_PARAMS };
407 *** 1) Add to trw_layer_params and enumeration
408 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
411 /****** END PARAMETERS ******/
413 static VikTrwLayer* trw_layer_new ( gint drawmode );
414 /* Layer Interface function definitions */
415 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
416 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
417 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
418 static void trw_layer_free ( VikTrwLayer *trwlayer );
419 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
420 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
421 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
422 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
423 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
424 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
425 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
426 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
427 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
428 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
429 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
430 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
431 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
432 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
433 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
434 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
435 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
436 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
437 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
438 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
439 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
440 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
441 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
442 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
443 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
444 /* End Layer Interface function definitions */
446 VikLayerInterface vik_trw_layer_interface = {
451 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
455 params_groups, /* params_groups */
456 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
460 (VikLayerFuncCreate) trw_layer_create,
461 (VikLayerFuncRealize) trw_layer_realize,
462 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
463 (VikLayerFuncFree) trw_layer_free,
465 (VikLayerFuncProperties) NULL,
466 (VikLayerFuncDraw) trw_layer_draw,
467 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
469 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
470 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
472 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
473 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
475 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
476 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
477 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
478 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
479 (VikLayerFuncLayerSelected) trw_layer_selected,
481 (VikLayerFuncMarshall) trw_layer_marshall,
482 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
484 (VikLayerFuncSetParam) trw_layer_set_param,
485 (VikLayerFuncGetParam) trw_layer_get_param,
487 (VikLayerFuncReadFileData) a_gpspoint_read_file,
488 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
490 (VikLayerFuncDeleteItem) trw_layer_del_item,
491 (VikLayerFuncCutItem) trw_layer_cut_item,
492 (VikLayerFuncCopyItem) trw_layer_copy_item,
493 (VikLayerFuncPasteItem) trw_layer_paste_item,
494 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
496 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
498 (VikLayerFuncSelectClick) trw_layer_select_click,
499 (VikLayerFuncSelectMove) trw_layer_select_move,
500 (VikLayerFuncSelectRelease) trw_layer_select_release,
501 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
504 /* for copy & paste (I think?) */
512 GType vik_trw_layer_get_type ()
514 static GType vtl_type = 0;
518 static const GTypeInfo vtl_info =
520 sizeof (VikTrwLayerClass),
521 NULL, /* base_init */
522 NULL, /* base_finalize */
523 NULL, /* class init */
524 NULL, /* class_finalize */
525 NULL, /* class_data */
526 sizeof (VikTrwLayer),
528 NULL /* instance init */
530 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
536 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
538 static gpointer pass_along[6];
544 pass_along[1] = NULL;
545 pass_along[2] = GINT_TO_POINTER (subtype);
546 pass_along[3] = sublayer;
547 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
548 pass_along[5] = NULL;
550 trw_layer_delete_item ( pass_along );
553 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
555 static gpointer pass_along[6];
561 pass_along[1] = NULL;
562 pass_along[2] = GINT_TO_POINTER (subtype);
563 pass_along[3] = sublayer;
564 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
565 pass_along[5] = NULL;
567 trw_layer_copy_item_cb(pass_along);
568 trw_layer_cut_item_cb(pass_along);
571 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
573 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
574 gint subtype = GPOINTER_TO_INT (pass_along[2]);
575 gpointer * sublayer = pass_along[3];
579 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
582 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
583 subtype, len, (const gchar*) sublayer, data);
587 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
589 trw_layer_copy_item_cb(pass_along);
590 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
591 trw_layer_delete_item(pass_along);
594 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
605 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
607 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
609 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
612 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
613 fi = g_malloc ( *len );
614 fi->len = strlen(sublayer) + 1;
615 memcpy(fi->data, sublayer, fi->len);
616 memcpy(fi->data + fi->len, id, il);
618 *item = (guint8 *)fi;
621 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
623 FlatItem *fi = (FlatItem *) item;
625 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
630 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
631 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
632 vik_trw_layer_add_waypoint ( vtl, name, w );
633 waypoint_convert(name, w, &vtl->coord_mode);
634 // Consider if redraw necessary for the new item
635 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
636 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
639 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
643 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
644 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
645 vik_trw_layer_add_track ( vtl, name, t );
646 track_convert(name, t, &vtl->coord_mode);
647 // Consider if redraw necessary for the new item
648 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
649 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
655 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
662 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
666 case PARAM_TV: vtl->tracks_visible = data.b; break;
667 case PARAM_WV: vtl->waypoints_visible = data.b; break;
668 case PARAM_DM: vtl->drawmode = data.u; break;
669 case PARAM_DP: vtl->drawpoints = data.b; break;
670 case PARAM_DE: vtl->drawelevation = data.b; break;
671 case PARAM_DS: vtl->drawstops = data.b; break;
672 case PARAM_DL: vtl->drawlines = data.b; break;
673 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
674 vtl->stop_length = data.u;
676 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
677 vtl->elevation_factor = data.u;
679 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
681 vtl->line_thickness = data.u;
682 trw_layer_new_track_gcs ( vtl, vp );
685 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
687 vtl->bg_line_thickness = data.u;
688 trw_layer_new_track_gcs ( vtl, vp );
693 /* Convert to store internally
694 NB file operation always in internal units (metres per second) */
695 vik_units_speed_t speed_units = a_vik_get_units_speed ();
696 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
697 vtl->velocity_min = data.d;
698 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
699 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
700 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
701 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
704 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
709 /* Convert to store internally
710 NB file operation always in internal units (metres per second) */
711 vik_units_speed_t speed_units = a_vik_get_units_speed ();
712 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
713 vtl->velocity_max = data.d;
714 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
715 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
716 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
717 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
720 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
723 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
724 case PARAM_DLA: vtl->drawlabels = data.b; break;
725 case PARAM_DI: vtl->drawimages = data.b; break;
726 case PARAM_IS: if ( data.u != vtl->image_size )
728 vtl->image_size = data.u;
729 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
730 g_queue_free ( vtl->image_cache );
731 vtl->image_cache = g_queue_new ();
734 case PARAM_IA: vtl->image_alpha = data.u; break;
735 case PARAM_ICS: vtl->image_cache_size = data.u;
736 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
737 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
739 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
740 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
741 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
742 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
743 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
744 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
745 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
750 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
752 VikLayerParamData rv;
755 case PARAM_TV: rv.b = vtl->tracks_visible; break;
756 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
757 case PARAM_DM: rv.u = vtl->drawmode; break;
758 case PARAM_DP: rv.b = vtl->drawpoints; break;
759 case PARAM_DE: rv.b = vtl->drawelevation; break;
760 case PARAM_EF: rv.u = vtl->elevation_factor; break;
761 case PARAM_DS: rv.b = vtl->drawstops; break;
762 case PARAM_SL: rv.u = vtl->stop_length; break;
763 case PARAM_DL: rv.b = vtl->drawlines; break;
764 case PARAM_LT: rv.u = vtl->line_thickness; break;
765 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
768 /* Convert to store internally
769 NB file operation always in internal units (metres per second) */
770 vik_units_speed_t speed_units = a_vik_get_units_speed ();
771 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
772 rv.d = vtl->velocity_min;
773 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
774 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
775 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
776 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
779 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
784 /* Convert to store internally
785 NB file operation always in internal units (metres per second) */
786 vik_units_speed_t speed_units = a_vik_get_units_speed ();
787 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
788 rv.d = vtl->velocity_max;
789 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
790 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
791 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
792 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
795 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
798 case PARAM_DLA: rv.b = vtl->drawlabels; break;
799 case PARAM_DI: rv.b = vtl->drawimages; break;
800 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
801 case PARAM_IS: rv.u = vtl->image_size; break;
802 case PARAM_IA: rv.u = vtl->image_alpha; break;
803 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
804 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
805 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
806 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
807 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
808 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
809 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
810 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
815 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
826 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
827 a_gpx_write_file(vtl, f);
828 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
831 g_file_get_contents(tmpname, &dd, &dl, NULL);
832 *len = sizeof(pl) + pl + dl;
833 *data = g_malloc(*len);
834 memcpy(*data, &pl, sizeof(pl));
835 memcpy(*data + sizeof(pl), pd, pl);
836 memcpy(*data + sizeof(pl) + pl, dd, dl);
845 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
847 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
853 memcpy(&pl, data, sizeof(pl));
855 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
858 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
859 g_critical("couldn't open temp file");
862 fwrite(data, len - pl - sizeof(pl), 1, f);
864 a_gpx_read_file(rv, f);
872 static GList * str_array_to_glist(gchar* data[])
876 for (p = (gpointer)data; *p; p++)
877 gl = g_list_prepend(gl, *p);
878 return(g_list_reverse(gl));
881 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
883 return (strcasecmp(s1, s2) == 0);
886 static guint strcase_hash(gconstpointer v)
888 /* 31 bit hash function */
891 gchar s[128]; /* malloc is too slow for reading big files */
894 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
895 p[i] = toupper(t[i]);
901 for (p += 1; *p != '\0'; p++)
902 h = (h << 5) - h + *p;
908 static VikTrwLayer* trw_layer_new ( gint drawmode )
910 if (trw_layer_params[PARAM_DM].widget_data == NULL)
911 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
912 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
913 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
915 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
916 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
918 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
919 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
920 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
921 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
923 /* TODO: constants at top */
924 rv->waypoints_visible = rv->tracks_visible = TRUE;
925 rv->drawmode = drawmode;
926 rv->drawpoints = TRUE;
927 rv->drawstops = FALSE;
928 rv->drawelevation = FALSE;
929 rv->elevation_factor = 30;
930 rv->stop_length = 60;
931 rv->drawlines = TRUE;
932 rv->wplabellayout = NULL;
933 rv->wp_right_click_menu = NULL;
934 rv->track_right_click_menu = NULL;
935 rv->waypoint_gc = NULL;
936 rv->waypoint_text_gc = NULL;
937 rv->waypoint_bg_gc = NULL;
939 rv->velocity_max = 5.0;
940 rv->velocity_min = 0.0;
941 rv->line_thickness = 1;
942 rv->bg_line_thickness = 0;
943 rv->current_wp = NULL;
944 rv->current_wp_name = NULL;
945 rv->current_track = NULL;
946 rv->current_tpl = NULL;
947 rv->current_tp_track_name = NULL;
948 rv->moving_tp = FALSE;
949 rv->moving_wp = FALSE;
951 rv->ct_sync_done = TRUE;
953 rv->route_finder_started = FALSE;
954 rv->route_finder_check_added_track = FALSE;
955 rv->route_finder_added_track_name = NULL;
956 rv->route_finder_current_track = NULL;
957 rv->route_finder_append = FALSE;
959 rv->waypoint_rightclick = FALSE;
961 rv->last_tp_track_name = NULL;
963 rv->image_cache = g_queue_new();
965 rv->image_alpha = 255;
966 rv->image_cache_size = 300;
967 rv->drawimages = TRUE;
968 rv->drawlabels = TRUE;
973 static void trw_layer_free ( VikTrwLayer *trwlayer )
975 g_hash_table_destroy(trwlayer->waypoints);
976 g_hash_table_destroy(trwlayer->tracks);
978 /* ODC: replace with GArray */
979 trw_layer_free_track_gcs ( trwlayer );
981 if ( trwlayer->wp_right_click_menu )
982 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
984 if ( trwlayer->track_right_click_menu )
985 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
987 if ( trwlayer->wplabellayout != NULL)
988 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
990 if ( trwlayer->waypoint_gc != NULL )
991 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
993 if ( trwlayer->waypoint_text_gc != NULL )
994 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
996 if ( trwlayer->waypoint_bg_gc != NULL )
997 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
999 if ( trwlayer->waypoint_font != NULL )
1000 gdk_font_unref ( trwlayer->waypoint_font );
1002 if ( trwlayer->tpwin != NULL )
1003 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1005 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1006 g_queue_free ( trwlayer->image_cache );
1009 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
1012 dp->xmpp = vik_viewport_get_xmpp ( vp );
1013 dp->ympp = vik_viewport_get_ympp ( vp );
1014 dp->width = vik_viewport_get_width ( vp );
1015 dp->height = vik_viewport_get_height ( vp );
1016 dp->center = vik_viewport_get_center ( vp );
1017 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1018 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1023 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1024 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1025 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1027 dp->ce1 = dp->center->east_west-w2;
1028 dp->ce2 = dp->center->east_west+w2;
1029 dp->cn1 = dp->center->north_south-h2;
1030 dp->cn2 = dp->center->north_south+h2;
1031 } else if ( dp->lat_lon ) {
1032 VikCoord upperleft, bottomright;
1033 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1034 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1035 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1036 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1037 dp->ce1 = upperleft.east_west;
1038 dp->ce2 = bottomright.east_west;
1039 dp->cn1 = bottomright.north_south;
1040 dp->cn2 = upperleft.north_south;
1043 dp->track_gc_iter = 0;
1046 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1048 static gdouble rv = 0;
1049 if ( tp1->has_timestamp && tp2->has_timestamp )
1051 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1052 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1055 return VIK_TRW_LAYER_TRACK_GC_MIN;
1056 else if ( vtl->velocity_min >= vtl->velocity_max )
1057 return VIK_TRW_LAYER_TRACK_GC_MAX;
1059 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1061 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1062 return VIK_TRW_LAYER_TRACK_GC_MAX;
1066 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1069 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1071 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1072 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1073 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1074 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1077 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1079 /* TODO: this function is a mess, get rid of any redundancy */
1080 GList *list = track->trackpoints;
1082 gboolean useoldvals = TRUE;
1084 gboolean drawpoints;
1086 gboolean drawelevation;
1087 gdouble min_alt, max_alt, alt_diff = 0;
1089 const guint8 tp_size_reg = 2;
1090 const guint8 tp_size_cur = 4;
1093 if ( dp->vtl->drawelevation )
1095 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1096 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1097 alt_diff = max_alt - min_alt;
1100 if ( ! track->visible )
1103 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1104 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1105 trw_layer_draw_track ( name, track, dp, TRUE );
1107 if ( drawing_white_background )
1108 drawpoints = drawstops = FALSE;
1110 drawpoints = dp->vtl->drawpoints;
1111 drawstops = dp->vtl->drawstops;
1114 /* Current track - used for creation */
1115 if ( track == dp->vtl->current_track )
1116 main_gc = dp->vtl->current_track_gc;
1118 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1119 /* Draw all tracks of the layer in special colour */
1120 /* if track is member of selected layer or is the current selected track
1121 then draw in the highlight colour.
1122 NB this supercedes the drawmode */
1123 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1124 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1125 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1126 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1129 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1130 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1132 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1136 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1137 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1139 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1144 int x, y, oldx, oldy;
1145 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1147 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1149 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1151 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1153 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1154 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1160 while ((list = g_list_next(list)))
1162 tp = VIK_TRACKPOINT(list->data);
1163 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1165 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1166 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1167 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1168 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1169 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1171 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1173 if ( drawpoints && ! drawing_white_background )
1176 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1179 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1180 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 );
1183 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 );
1186 if ((!tp->newsegment) && (dp->vtl->drawlines))
1188 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1190 /* UTM only: zone check */
1191 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1192 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1194 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1195 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1196 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1200 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1202 if ( drawing_white_background ) {
1203 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1207 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1208 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1210 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1211 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1215 tmp[1].y = oldy-FIXALTITUDE(list->data);
1217 tmp[2].y = y-FIXALTITUDE(list->next->data);
1222 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1223 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1225 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1226 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1228 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1238 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1240 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1241 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1243 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1244 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1245 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1246 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1249 if ( drawing_white_background )
1250 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1252 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1256 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1257 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1264 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1265 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1266 dp->track_gc_iter = 0;
1269 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1270 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1272 trw_layer_draw_track ( name, track, dp, FALSE );
1275 static void cached_pixbuf_free ( CachedPixbuf *cp )
1277 g_object_unref ( G_OBJECT(cp->pixbuf) );
1278 g_free ( cp->image );
1281 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1283 return strcmp ( cp->image, name );
1286 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1289 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1290 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1291 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1294 GdkPixbuf *sym = NULL;
1295 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1297 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1299 if ( wp->image && dp->vtl->drawimages )
1301 GdkPixbuf *pixbuf = NULL;
1304 if ( dp->vtl->image_alpha == 0)
1307 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1309 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1312 gchar *image = wp->image;
1313 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1314 if ( ! regularthumb )
1316 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1317 image = "\x12\x00"; /* this shouldn't occur naturally. */
1321 CachedPixbuf *cp = NULL;
1322 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1323 if ( dp->vtl->image_size == 128 )
1324 cp->pixbuf = regularthumb;
1327 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1328 g_assert ( cp->pixbuf );
1329 g_object_unref ( G_OBJECT(regularthumb) );
1331 cp->image = g_strdup ( image );
1333 /* needed so 'click picture' tool knows how big the pic is; we don't
1334 * store it in cp because they may have been freed already. */
1335 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1336 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1338 g_queue_push_head ( dp->vtl->image_cache, cp );
1339 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1340 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1342 pixbuf = cp->pixbuf;
1346 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1352 w = gdk_pixbuf_get_width ( pixbuf );
1353 h = gdk_pixbuf_get_height ( pixbuf );
1355 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1357 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1358 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1359 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1360 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1361 // Highlighted - so draw a little border around the chosen one
1362 // single line seems a little weak so draw 2 of them
1363 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1364 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1365 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1366 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1369 if ( dp->vtl->image_alpha == 255 )
1370 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1372 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1374 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1378 /* DRAW ACTUAL DOT */
1379 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1380 vik_viewport_draw_pixbuf ( dp->vp, sym, 0, 0, x - gdk_pixbuf_get_width(sym)/2, y - gdk_pixbuf_get_height(sym)/2, -1, -1 );
1382 else if ( wp == dp->vtl->current_wp ) {
1383 switch ( dp->vtl->wp_symbol ) {
1384 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;
1385 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;
1386 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;
1387 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 );
1388 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 );
1392 switch ( dp->vtl->wp_symbol ) {
1393 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;
1394 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;
1395 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;
1396 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 );
1397 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;
1401 if ( dp->vtl->drawlabels )
1403 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1404 gint label_x, label_y;
1406 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1407 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1408 label_x = x - width/2;
1410 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1412 label_y = y - dp->vtl->wp_size - height - 2;
1414 /* if highlight mode on, then draw background text in highlight colour */
1415 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1416 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1417 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1418 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1419 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1421 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1424 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1426 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1431 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1433 static struct DrawingParams dp;
1434 g_assert ( l != NULL );
1436 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1439 if ( l->tracks_visible )
1440 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1442 if (l->waypoints_visible)
1443 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1446 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1449 if ( vtl->track_bg_gc )
1451 g_object_unref ( vtl->track_bg_gc );
1452 vtl->track_bg_gc = NULL;
1454 if ( vtl->current_track_gc )
1456 g_object_unref ( vtl->current_track_gc );
1457 vtl->current_track_gc = NULL;
1460 if ( ! vtl->track_gc )
1462 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1463 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1464 g_array_free ( vtl->track_gc, TRUE );
1465 vtl->track_gc = NULL;
1468 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1470 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1471 gint width = vtl->line_thickness;
1473 if ( vtl->track_gc )
1474 trw_layer_free_track_gcs ( vtl );
1476 if ( vtl->track_bg_gc )
1477 g_object_unref ( vtl->track_bg_gc );
1478 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1480 if ( vtl->current_track_gc )
1481 g_object_unref ( vtl->current_track_gc );
1482 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1483 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1485 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1487 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1489 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1490 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1491 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1492 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1493 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1494 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1495 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1496 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1497 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1498 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1500 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1502 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1504 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1507 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1509 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1510 PangoFontDescription *pfd;
1511 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1512 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1513 pango_layout_set_font_description (rv->wplabellayout, pfd);
1514 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1515 pango_font_description_free (pfd);
1517 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1519 trw_layer_new_track_gcs ( rv, vp );
1521 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1522 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1523 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1524 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1526 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1528 rv->has_verified_thumbnails = FALSE;
1529 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1531 rv->wp_draw_symbols = TRUE;
1533 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1535 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1540 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1542 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1544 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1545 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1547 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1550 *new_iter = *((GtkTreeIter *) pass_along[1]);
1551 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1553 if ( ! track->visible )
1554 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1557 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1559 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1560 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1561 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1563 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1566 *new_iter = *((GtkTreeIter *) pass_along[1]);
1567 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1569 if ( ! wp->visible )
1570 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1574 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1577 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1579 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1580 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1582 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1584 if ( ! vtl->tracks_visible )
1585 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1587 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1589 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1590 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1592 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1595 if ( ! vtl->waypoints_visible )
1596 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1598 pass_along[0] = &(vtl->waypoints_iter);
1599 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1601 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1605 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1609 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1610 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1611 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1613 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1615 return (t->visible ^= 1);
1619 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1621 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1623 return (t->visible ^= 1);
1632 * Return a property about tracks for this layer
1634 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1636 return vtl->line_thickness;
1639 // Structure to hold multiple track information for a layer
1648 * Build up layer multiple track information via updating the tooltip_tracks structure
1650 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1652 tt->length = tt->length + vik_track_get_length (tr);
1654 // Ensure times are available
1655 if ( tr->trackpoints &&
1656 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1657 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1660 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1661 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1663 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1664 // Hence initialize to the first 'proper' value
1665 if ( tt->start_time == 0 )
1666 tt->start_time = t1;
1667 if ( tt->end_time == 0 )
1670 // Update find the earliest / last times
1671 if ( t1 < tt->start_time )
1672 tt->start_time = t1;
1673 if ( t2 > tt->end_time )
1676 // Keep track of total time
1677 // there maybe gaps within a track (eg segments)
1678 // but this should be generally good enough for a simple indicator
1679 tt->duration = tt->duration + (int)(t2-t1);
1684 * Generate tooltip text for the layer.
1685 * This is relatively complicated as it considers information for
1686 * no tracks, a single track or multiple tracks
1687 * (which may or may not have timing information)
1689 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1700 static gchar tmp_buf[128];
1703 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1705 // Safety check - I think these should always be valid
1706 if ( vtl->tracks && vtl->waypoints ) {
1707 tooltip_tracks tt = { 0.0, 0, 0 };
1708 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1710 GDate* gdate_start = g_date_new ();
1711 g_date_set_time_t (gdate_start, tt.start_time);
1713 GDate* gdate_end = g_date_new ();
1714 g_date_set_time_t (gdate_end, tt.end_time);
1716 if ( g_date_compare (gdate_start, gdate_end) ) {
1717 // Dates differ so print range on separate line
1718 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1719 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1720 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1723 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1724 if ( tt.start_time != 0 )
1725 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1729 if ( tt.length > 0.0 ) {
1730 gdouble len_in_units;
1732 // Setup info dependent on distance units
1733 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1734 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1735 len_in_units = VIK_METERS_TO_MILES(tt.length);
1738 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1739 len_in_units = tt.length/1000.0;
1742 // Timing information if available
1744 if ( tt.duration > 0 ) {
1745 g_snprintf (tbuf1, sizeof(tbuf1),
1746 _(" in %d:%02d hrs:mins"),
1747 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1749 g_snprintf (tbuf2, sizeof(tbuf2),
1750 _("\n%sTotal Length %.1f %s%s"),
1751 tbuf3, len_in_units, tbuf4, tbuf1);
1754 // Put together all the elements to form compact tooltip text
1755 g_snprintf (tmp_buf, sizeof(tmp_buf),
1756 _("Tracks: %d - Waypoints: %d%s"),
1757 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1759 g_date_free (gdate_start);
1760 g_date_free (gdate_end);
1767 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1771 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1772 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1773 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1775 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1777 // Could be a better way of handling strings - but this works...
1778 gchar time_buf1[20];
1779 gchar time_buf2[20];
1780 time_buf1[0] = '\0';
1781 time_buf2[0] = '\0';
1782 static gchar tmp_buf[100];
1783 // Compact info: Short date eg (11/20/99), duration and length
1784 // Hopefully these are the things that are most useful and so promoted into the tooltip
1785 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1786 // %x The preferred date representation for the current locale without the time.
1787 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1788 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1789 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1791 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1794 // Get length and consider the appropriate distance units
1795 gdouble tr_len = vik_track_get_length(tr);
1796 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1797 switch (dist_units) {
1798 case VIK_UNITS_DISTANCE_KILOMETRES:
1799 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1801 case VIK_UNITS_DISTANCE_MILES:
1802 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1811 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1813 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1814 // NB It's OK to return NULL
1824 * Function to show basic track point information on the statusbar
1826 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1829 switch (a_vik_get_units_height ()) {
1830 case VIK_UNITS_HEIGHT_FEET:
1831 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1834 //VIK_UNITS_HEIGHT_METRES:
1835 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1840 if ( trkpt->has_timestamp ) {
1841 // Compact date time format
1842 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1846 // Position is put later on, as this bit may not be seen if the display is not big enough,
1847 // one can easily use the current pointer position to see this if needed
1848 gchar *lat = NULL, *lon = NULL;
1849 static struct LatLon ll;
1850 vik_coord_to_latlon (&(trkpt->coord), &ll);
1851 a_coords_latlon_to_string ( &ll, &lat, &lon );
1854 // Again is put later on, as this bit may not be seen if the display is not big enough
1855 // trackname can be seen from the treeview (when enabled)
1856 // Also name could be very long to not leave room for anything else
1859 if ( vtl->current_tp_track_name ) {
1860 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track_name );
1863 // Combine parts to make overall message
1864 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
1865 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1872 * Function to show basic waypoint information on the statusbar
1874 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
1877 switch (a_vik_get_units_height ()) {
1878 case VIK_UNITS_HEIGHT_FEET:
1879 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
1882 //VIK_UNITS_HEIGHT_METRES:
1883 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
1887 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
1888 // one can easily use the current pointer position to see this if needed
1889 gchar *lat = NULL, *lon = NULL;
1890 static struct LatLon ll;
1891 vik_coord_to_latlon (&(wpt->coord), &ll);
1892 a_coords_latlon_to_string ( &ll, &lat, &lon );
1894 // Combine parts to make overall message
1897 // Add comment if available
1898 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
1900 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
1901 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1908 * General layer selection function, find out which bit is selected and take appropriate action
1910 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1913 l->current_wp = NULL;
1914 l->current_wp_name = NULL;
1915 trw_layer_cancel_current_tp ( l, FALSE );
1918 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
1922 case VIK_TREEVIEW_TYPE_LAYER:
1924 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1925 /* Mark for redraw */
1930 case VIK_TREEVIEW_TYPE_SUBLAYER:
1934 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1936 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1937 /* Mark for redraw */
1941 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1943 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
1944 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
1945 /* Mark for redraw */
1949 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1951 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
1952 /* Mark for redraw */
1956 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1958 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
1959 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
1960 // Show some waypoint info
1961 set_statusbar_msg_info_wpt ( l, wpt );
1962 /* Mark for redraw */
1968 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1977 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1982 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1987 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1989 return l->waypoints;
1992 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1994 static VikCoord fixme;
1995 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1996 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1997 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1998 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1999 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2000 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2001 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2002 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2003 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2006 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
2009 static VikCoord fixme;
2013 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2014 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2015 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2016 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2017 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2018 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2019 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2020 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2021 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2026 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2028 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2029 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2031 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2032 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2033 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2034 maxmin[0].lat = wpt_maxmin[0].lat;
2037 maxmin[0].lat = trk_maxmin[0].lat;
2039 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2040 maxmin[0].lon = wpt_maxmin[0].lon;
2043 maxmin[0].lon = trk_maxmin[0].lon;
2045 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2046 maxmin[1].lat = wpt_maxmin[1].lat;
2049 maxmin[1].lat = trk_maxmin[1].lat;
2051 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2052 maxmin[1].lon = wpt_maxmin[1].lon;
2055 maxmin[1].lon = trk_maxmin[1].lon;
2059 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2061 /* 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... */
2062 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2063 trw_layer_find_maxmin (vtl, maxmin);
2064 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2068 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2069 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2074 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2077 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2078 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2080 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2083 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2085 /* First set the center [in case previously viewing from elsewhere] */
2086 /* Then loop through zoom levels until provided positions are in view */
2087 /* This method is not particularly fast - but should work well enough */
2088 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2090 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2091 vik_viewport_set_center_coord ( vvp, &coord );
2093 /* Convert into definite 'smallest' and 'largest' positions */
2094 struct LatLon minmin;
2095 if ( maxmin[0].lat < maxmin[1].lat )
2096 minmin.lat = maxmin[0].lat;
2098 minmin.lat = maxmin[1].lat;
2100 struct LatLon maxmax;
2101 if ( maxmin[0].lon > maxmin[1].lon )
2102 maxmax.lon = maxmin[0].lon;
2104 maxmax.lon = maxmin[1].lon;
2106 /* Never zoom in too far - generally not that useful, as too close ! */
2107 /* Always recalculate the 'best' zoom level */
2109 vik_viewport_set_zoom ( vvp, zoom );
2111 gdouble min_lat, max_lat, min_lon, max_lon;
2112 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2113 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2114 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2115 /* NB I think the logic used in this test to determine if the bounds is within view
2116 fails if track goes across 180 degrees longitude.
2117 Hopefully that situation is not too common...
2118 Mind you viking doesn't really do edge locations to well anyway */
2119 if ( min_lat < minmin.lat &&
2120 max_lat > minmin.lat &&
2121 min_lon < maxmax.lon &&
2122 max_lon > maxmax.lon )
2123 /* Found within zoom level */
2128 vik_viewport_set_zoom ( vvp, zoom );
2132 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2134 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2135 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2136 trw_layer_find_maxmin (vtl, maxmin);
2137 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2140 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2145 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2147 if ( vik_trw_layer_auto_set_view ( VIK_TRW_LAYER(layer_and_vlp[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(layer_and_vlp[1])) ) ) {
2148 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2151 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2154 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2156 GtkWidget *file_selector;
2158 gboolean failed = FALSE;
2159 file_selector = gtk_file_chooser_dialog_new (title,
2161 GTK_FILE_CHOOSER_ACTION_SAVE,
2162 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2163 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2165 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2167 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2169 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2170 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2172 gtk_widget_hide ( file_selector );
2173 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2178 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2180 gtk_widget_hide ( file_selector );
2181 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2186 gtk_widget_destroy ( file_selector );
2188 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2191 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2193 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2196 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2198 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2201 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2203 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2204 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2205 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2206 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2208 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2210 g_free ( auto_save_name );
2213 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2215 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2216 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2217 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2218 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2220 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2222 g_free ( auto_save_name );
2225 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2227 gpointer layer_and_vlp[2];
2228 layer_and_vlp[0] = pass_along[0];
2229 layer_and_vlp[1] = pass_along[1];
2231 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2232 gchar *auto_save_name = g_strdup ( pass_along[3] );
2233 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2234 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2236 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2238 g_free ( auto_save_name );
2241 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2243 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2244 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2245 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2246 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2248 GTK_RESPONSE_REJECT,
2250 GTK_RESPONSE_ACCEPT,
2253 GtkWidget *label, *entry;
2254 label = gtk_label_new(_("Waypoint Name:"));
2255 entry = gtk_entry_new();
2257 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2258 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2259 gtk_widget_show_all ( label );
2260 gtk_widget_show_all ( entry );
2262 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2264 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2267 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2270 for ( i = strlen(upname)-1; i >= 0; i-- )
2271 upname[i] = toupper(upname[i]);
2273 wp = g_hash_table_lookup ( wps, upname );
2276 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2279 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2280 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2281 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, upname ), TRUE );
2288 gtk_widget_destroy ( dia );
2291 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2293 gchar *default_name = highest_wp_number_get(vtl);
2294 VikWaypoint *wp = vik_waypoint_new();
2295 gchar *returned_name;
2297 wp->coord = *def_coord;
2299 // Attempt to auto set height if DEM data is available
2300 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2301 if ( elev != VIK_DEM_INVALID_ELEVATION )
2302 wp->altitude = (gdouble)elev;
2304 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2307 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2308 g_free (default_name);
2311 g_free (default_name);
2312 vik_waypoint_free(wp);
2316 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2319 struct LatLon one_ll, two_ll;
2320 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2322 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2323 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2324 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2325 VikViewport *vvp = vik_window_viewport(vw);
2326 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2327 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2328 vik_coord_to_latlon(&one, &one_ll);
2329 vik_coord_to_latlon(&two, &two_ll);
2330 if (one_ll.lat > two_ll.lat) {
2331 maxmin[0].lat = one_ll.lat;
2332 maxmin[1].lat = two_ll.lat;
2335 maxmin[0].lat = two_ll.lat;
2336 maxmin[1].lat = one_ll.lat;
2338 if (one_ll.lon > two_ll.lon) {
2339 maxmin[0].lon = one_ll.lon;
2340 maxmin[1].lon = two_ll.lon;
2343 maxmin[0].lon = two_ll.lon;
2344 maxmin[1].lon = one_ll.lon;
2346 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2349 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2351 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2352 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2353 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2355 trw_layer_find_maxmin (vtl, maxmin);
2356 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2359 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2362 * Acquire into this TRW Layer straight from GPS Device
2364 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2366 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2367 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2368 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2369 VikViewport *vvp = vik_window_viewport(vw);
2371 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2372 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2376 * Acquire into this TRW Layer from Google Directions
2378 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2380 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2381 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2382 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2383 VikViewport *vvp = vik_window_viewport(vw);
2385 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2388 #ifdef VIK_CONFIG_OPENSTREETMAP
2390 * Acquire into this TRW Layer from OSM
2392 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2394 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2395 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2396 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2397 VikViewport *vvp = vik_window_viewport(vw);
2399 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2403 #ifdef VIK_CONFIG_GEOCACHES
2405 * Acquire into this TRW Layer from Geocaching.com
2407 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2409 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2410 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2411 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2412 VikViewport *vvp = vik_window_viewport(vw);
2414 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2418 static void trw_layer_new_wp ( gpointer lav[2] )
2420 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2421 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2422 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2423 instead return true if you want to update. */
2424 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 )
2425 vik_layers_panel_emit_update ( vlp );
2428 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2430 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2431 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2433 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2434 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2435 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2436 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2437 vik_layers_panel_emit_update ( vlp );
2441 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2443 /* NB do not care if wp is visible or not */
2444 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2447 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2449 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2450 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2452 /* Only 1 waypoint - jump straight to it */
2453 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2454 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2455 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2457 /* If at least 2 waypoints - find center and then zoom to fit */
2458 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2460 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2461 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2462 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2465 vik_layers_panel_emit_update ( vlp );
2468 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2470 static gpointer pass_along[2];
2472 GtkWidget *export_submenu;
2473 GtkWidget *wikipedia_submenu;
2474 pass_along[0] = vtl;
2475 pass_along[1] = vlp;
2477 item = gtk_menu_item_new();
2478 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2479 gtk_widget_show ( item );
2481 /* Now with icons */
2482 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2483 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2484 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2485 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2486 gtk_widget_show ( item );
2488 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2490 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2491 gtk_widget_show ( item );
2493 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2495 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2496 gtk_widget_show ( item );
2498 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2499 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2500 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2501 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2502 gtk_widget_show ( item );
2504 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2505 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2506 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2507 gtk_widget_show ( item );
2509 export_submenu = gtk_menu_new ();
2510 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2512 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2513 gtk_widget_show ( item );
2514 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2516 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2517 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2518 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2519 gtk_widget_show ( item );
2521 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2522 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2523 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2524 gtk_widget_show ( item );
2526 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2527 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2528 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2529 gtk_widget_show ( item );
2531 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2532 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2533 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2534 gtk_widget_show ( item );
2536 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2537 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2538 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2539 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2540 gtk_widget_show ( item );
2542 #ifdef VIK_CONFIG_GEONAMES
2543 wikipedia_submenu = gtk_menu_new();
2544 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2545 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2546 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2547 gtk_widget_show(item);
2548 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2550 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2551 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2552 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2553 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2554 gtk_widget_show ( item );
2556 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2557 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2558 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2559 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2560 gtk_widget_show ( item );
2563 GtkWidget *acquire_submenu = gtk_menu_new ();
2564 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2565 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2566 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2567 gtk_widget_show ( item );
2568 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2570 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2572 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2573 gtk_widget_show ( item );
2575 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2576 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2577 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2578 gtk_widget_show ( item );
2580 #ifdef VIK_CONFIG_OPENSTREETMAP
2581 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2582 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2583 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2584 gtk_widget_show ( item );
2587 #ifdef VIK_CONFIG_GEOCACHES
2588 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2590 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2591 gtk_widget_show ( item );
2594 #ifdef VIK_CONFIG_OPENSTREETMAP
2595 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2596 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2597 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2598 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2599 gtk_widget_show ( item );
2602 GtkWidget *delete_submenu = gtk_menu_new ();
2603 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2604 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2605 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2606 gtk_widget_show ( item );
2607 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2609 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2610 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2612 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2613 gtk_widget_show ( item );
2615 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2616 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2617 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2618 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2619 gtk_widget_show ( item );
2621 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2622 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2623 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2624 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2625 gtk_widget_show ( item );
2627 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2628 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2630 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2631 gtk_widget_show ( item );
2633 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2634 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2636 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2637 gtk_widget_show ( item );
2640 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2641 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2643 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2644 gtk_widget_show ( item );
2648 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2650 if ( VIK_LAYER(vtl)->realized )
2652 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2654 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2657 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2658 // Visibility column always needed for waypoints
2659 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2660 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2662 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2664 // Actual setting of visibility dependent on the waypoint
2665 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2666 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2670 highest_wp_number_add_wp(vtl, name);
2671 g_hash_table_insert ( vtl->waypoints, name, wp );
2675 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2677 if ( VIK_LAYER(vtl)->realized )
2679 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2681 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2684 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2685 // Visibility column always needed for tracks
2686 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2687 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2689 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2691 // Actual setting of visibility dependent on the track
2692 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2693 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2697 g_hash_table_insert ( vtl->tracks, name, t );
2701 /* to be called whenever a track has been deleted or may have been changed. */
2702 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2704 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2705 trw_layer_cancel_current_tp ( vtl, FALSE );
2706 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2707 trw_layer_cancel_last_tp ( vtl );
2710 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2713 gchar *newname = g_strdup(name);
2714 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2715 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2716 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2718 newname = new_newname;
2724 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2726 vik_trw_layer_add_waypoint ( vtl,
2727 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2730 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2732 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
2733 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2734 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
2735 vik_track_free ( tr );
2736 vtl->route_finder_append = FALSE; /* this means we have added it */
2738 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2739 vik_trw_layer_add_track ( vtl, new_name, tr );
2741 if ( vtl->route_finder_check_added_track ) {
2742 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2743 if ( vtl->route_finder_added_track_name ) /* for google routes */
2744 g_free ( vtl->route_finder_added_track_name );
2745 vtl->route_finder_added_track_name = g_strdup(new_name);
2750 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2752 *l = g_list_append(*l, (gpointer)name);
2755 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2757 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2758 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2760 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2761 vik_trw_layer_delete_track(vtl_src, name);
2762 vik_trw_layer_add_track(vtl_dest, newname, t);
2764 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2766 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2767 vik_trw_layer_delete_waypoint(vtl_src, name);
2768 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2772 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2774 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2775 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2777 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2778 GList *items = NULL;
2781 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2782 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2784 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2785 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2790 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2791 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2793 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2800 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2801 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2805 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2807 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2808 gboolean was_visible = FALSE;
2812 was_visible = t->visible;
2813 if ( t == vtl->current_track ) {
2814 vtl->current_track = NULL;
2816 if ( t == vtl->route_finder_current_track )
2817 vtl->route_finder_current_track = NULL;
2819 /* could be current_tp, so we have to check */
2820 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2822 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2823 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2824 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2826 /* do this last because trk_name may be pointing to actual orig key */
2827 g_hash_table_remove ( vtl->tracks, trk_name );
2832 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2834 gboolean was_visible = FALSE;
2837 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2841 if ( wp == vtl->current_wp ) {
2842 vtl->current_wp = NULL;
2843 vtl->current_wp_name = NULL;
2844 vtl->moving_wp = FALSE;
2847 was_visible = wp->visible;
2848 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2849 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2850 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2852 highest_wp_number_remove_wp(vtl, wp_name);
2853 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2859 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2861 vik_treeview_item_delete (vt, it );
2864 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2867 vtl->current_track = NULL;
2868 vtl->route_finder_current_track = NULL;
2869 if (vtl->current_tp_track_name)
2870 trw_layer_cancel_current_tp(vtl, FALSE);
2871 if (vtl->last_tp_track_name)
2872 trw_layer_cancel_last_tp ( vtl );
2874 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2875 g_hash_table_remove_all(vtl->tracks_iters);
2876 g_hash_table_remove_all(vtl->tracks);
2878 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2881 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2883 vtl->current_wp = NULL;
2884 vtl->current_wp_name = NULL;
2885 vtl->moving_wp = FALSE;
2887 highest_wp_number_reset(vtl);
2889 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2890 g_hash_table_remove_all(vtl->waypoints_iters);
2891 g_hash_table_remove_all(vtl->waypoints);
2893 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2896 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
2898 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2899 // Get confirmation from the user
2900 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2901 _("Are you sure you want to delete all tracks in %s?"),
2902 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2903 vik_trw_layer_delete_all_tracks (vtl);
2906 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
2908 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2909 // Get confirmation from the user
2910 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2911 _("Are you sure you want to delete all waypoints in %s?"),
2912 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2913 vik_trw_layer_delete_all_waypoints (vtl);
2916 static void trw_layer_delete_item ( gpointer pass_along[6] )
2918 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2919 gboolean was_visible = FALSE;
2920 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2922 if ( GPOINTER_TO_INT ( pass_along[4]) )
2923 // Get confirmation from the user
2924 // Maybe this Waypoint Delete should be optional as is it could get annoying...
2925 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2926 _("Are you sure you want to delete the waypoint \"%s\""),
2929 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2933 if ( GPOINTER_TO_INT ( pass_along[4]) )
2934 // Get confirmation from the user
2935 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2936 _("Are you sure you want to delete the track \"%s\""),
2939 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2942 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2946 static void trw_layer_properties_item ( gpointer pass_along[6] )
2948 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2949 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2951 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2954 gboolean updated = FALSE;
2955 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
2957 if ( updated && VIK_LAYER(vtl)->visible )
2958 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2963 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2966 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2968 pass_along[1], /* vlp */
2969 pass_along[3], /* track name */
2970 pass_along[5] ); /* vvp */
2976 Parameter 1 -> VikLayersPanel
2977 Parameter 2 -> VikLayer
2978 Parameter 3 -> VikViewport
2980 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2983 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2984 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2987 /* since vlp not set, vl & vvp should be valid instead! */
2989 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2990 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
2995 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
2997 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2998 if ( trps && trps->data )
2999 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3002 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3004 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
3005 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3006 if ( trps && *trps )
3008 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3010 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3011 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3012 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3013 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3014 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3018 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3020 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3021 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3023 vtl->current_track = track;
3024 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3026 if ( track->trackpoints )
3027 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3031 * extend a track using route finder
3033 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3035 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3036 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3037 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3039 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3040 vtl->route_finder_coord = last_coord;
3041 vtl->route_finder_current_track = track;
3042 vtl->route_finder_started = TRUE;
3044 if ( track->trackpoints )
3045 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3049 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3051 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3052 /* Also warn if overwrite old elevation data */
3053 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3055 vik_track_apply_dem_data ( track );
3058 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3060 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3063 trps = g_list_last(trps);
3064 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3067 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3069 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3072 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3075 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3077 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3080 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3083 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3085 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3088 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3092 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3094 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
3096 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3097 if ( trps && *trps )
3099 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3100 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3101 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3102 if ( pass_along[1] )
3103 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3105 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3109 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3111 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3112 trw_layer_tpwin_init ( vtl );
3115 /*************************************
3116 * merge/split by time routines
3117 *************************************/
3119 /* called for each key in track hash table.
3120 * If the current track has the same time stamp type, add it to the result,
3121 * except the one pointed by "exclude".
3122 * set exclude to NULL if there is no exclude to check.
3123 * Note that the result is in reverse (for performance reasons).
3128 gboolean with_timestamps;
3130 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3132 twt_udata *user_data = udata;
3133 VikTrackpoint *p1, *p2;
3135 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3139 if (VIK_TRACK(value)->trackpoints) {
3140 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3141 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3143 if ( user_data->with_timestamps ) {
3144 if (!p1->has_timestamp || !p2->has_timestamp) {
3149 // Don't add tracks with timestamps when getting non timestamp tracks
3150 if (p1->has_timestamp || p2->has_timestamp) {
3156 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3159 /* called for each key in track hash table. if original track user_data[1] is close enough
3160 * to the passed one, add it to list in user_data[0]
3162 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3165 VikTrackpoint *p1, *p2;
3167 GList **nearby_tracks = ((gpointer *)user_data)[0];
3168 GList *orig_track = ((gpointer *)user_data)[1];
3169 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3172 * detect reasons for not merging, and return
3173 * if no reason is found not to merge, then do it.
3176 if (VIK_TRACK(value)->trackpoints == orig_track) {
3180 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3181 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3183 if (VIK_TRACK(value)->trackpoints) {
3184 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3185 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3187 if (!p1->has_timestamp || !p2->has_timestamp) {
3188 g_print("no timestamp\n");
3192 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3193 if (! (abs(t1 - p2->timestamp) < thr*60 ||
3195 abs(p1->timestamp - t2) < thr*60)
3202 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3205 /* comparison function used to sort tracks; a and b are hash table keys */
3206 /* Not actively used - can be restored if needed
3207 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3209 GHashTable *tracks = user_data;
3212 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3213 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3215 if (t1 < t2) return -1;
3216 if (t1 > t2) return 1;
3221 /* comparison function used to sort trackpoints */
3222 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3224 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3226 if (t1 < t2) return -1;
3227 if (t1 > t2) return 1;
3231 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3233 * comparison function which can be used to sort tracks or waypoints by name
3235 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3237 const gchar* namea = (const gchar*) a;
3238 const gchar* nameb = (const gchar*) b;
3239 if ( namea == NULL || nameb == NULL)
3242 // Same sort method as used in the vik_treeview_*_alphabetize functions
3243 return strcmp ( namea, nameb );
3248 * Attempt to merge selected track with other tracks specified by the user
3249 * Tracks to merge with must be of the same 'type' as the selected track -
3250 * either all with timestamps, or all without timestamps
3252 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3254 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3255 gchar *orig_track_name = pass_along[3];
3256 GList *other_tracks = NULL;
3257 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3259 if ( !track->trackpoints )
3263 udata.result = &other_tracks;
3264 udata.exclude = track->trackpoints;
3265 // Allow merging with 'similar' time type time tracks
3266 // i.e. either those times, or those without
3267 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3269 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3270 other_tracks = g_list_reverse(other_tracks);
3272 if ( !other_tracks ) {
3273 if ( udata.with_timestamps )
3274 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3276 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3280 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3281 // Sort alphabetically for user presentation
3282 other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3285 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3287 _("Merge with..."), _("Select track to merge with"));
3288 g_list_free(other_tracks);
3293 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3294 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3296 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3297 merge_track->trackpoints = NULL;
3298 vik_trw_layer_delete_track(vtl, l->data);
3299 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3302 /* TODO: free data before free merge_list */
3303 for (l = merge_list; l != NULL; l = g_list_next(l))
3305 g_list_free(merge_list);
3306 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3310 /* merge by time routine */
3311 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3313 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3314 gchar *orig_track_name = strdup(pass_along[3]);
3317 GList *nearby_tracks;
3320 static guint thr = 1;
3321 guint track_count = 0;
3323 GList *tracks_with_timestamp = NULL;
3324 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3325 if (track->trackpoints &&
3326 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3327 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3328 free(orig_track_name);
3333 udata.result = &tracks_with_timestamp;
3334 udata.exclude = track->trackpoints;
3335 udata.with_timestamps = TRUE;
3336 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3337 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3339 if (!tracks_with_timestamp) {
3340 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3341 free(orig_track_name);
3344 g_list_free(tracks_with_timestamp);
3346 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3347 _("Merge Threshold..."),
3348 _("Merge when time between tracks less than:"),
3350 free(orig_track_name);
3354 /* merge tracks until we can't */
3355 nearby_tracks = NULL;
3359 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3360 trps = track->trackpoints;
3365 if (nearby_tracks) {
3366 g_list_free(nearby_tracks);
3367 nearby_tracks = NULL;
3370 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3371 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3373 /* g_print("Original track times: %d and %d\n", t1, t2); */
3374 params[0] = &nearby_tracks;
3376 params[2] = GUINT_TO_POINTER (thr);
3378 /* get a list of adjacent-in-time tracks */
3379 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3381 /* add original track */
3382 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3386 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3387 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3388 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3389 GList *l = nearby_tracks;
3390 VikTrack *tr = vik_track_new();
3391 tr->visible = track->visible;
3396 t1 = get_first_trackpoint(l)->timestamp;
3397 t2 = get_last_trackpoint(l)->timestamp;
3398 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3402 /* remove trackpoints from merged track, delete track */
3403 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3404 get_track(l)->trackpoints = NULL;
3405 vik_trw_layer_delete_track(vtl, l->data);
3410 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3411 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3413 #undef get_first_trackpoint
3414 #undef get_last_trackpoint
3417 } while (track_count > 1);
3418 g_list_free(nearby_tracks);
3419 free(orig_track_name);
3420 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3423 /* split by time routine */
3424 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3426 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3427 GList *trps = track->trackpoints;
3429 GList *newlists = NULL;
3430 GList *newtps = NULL;
3432 static guint thr = 1;
3439 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3440 _("Split Threshold..."),
3441 _("Split when time between trackpoints exceeds:"),
3446 /* iterate through trackpoints, and copy them into new lists without touching original list */
3447 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3451 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3453 g_print("panic: ts < prev_ts: this should never happen!\n");
3456 if (ts - prev_ts > thr*60) {
3457 /* flush accumulated trackpoints into new list */
3458 newlists = g_list_append(newlists, g_list_reverse(newtps));
3462 /* accumulate trackpoint copies in newtps, in reverse order */
3463 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3465 iter = g_list_next(iter);
3468 newlists = g_list_append(newlists, g_list_reverse(newtps));
3471 /* put lists of trackpoints into tracks */
3474 // Only bother updating if the split results in new tracks
3475 if (g_list_length (newlists) > 1) {
3480 tr = vik_track_new();
3481 tr->visible = track->visible;
3482 tr->trackpoints = (GList *)(iter->data);
3484 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3485 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3486 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3487 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3489 iter = g_list_next(iter);
3491 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3492 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3494 g_list_free(newlists);
3498 * Split a track by the number of points as specified by the user
3500 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3502 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3503 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3505 // Check valid track
3506 GList *trps = track->trackpoints;
3510 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3511 _("Split Every Nth Point"),
3512 _("Split on every Nth point:"),
3513 250, // Default value as per typical limited track capacity of various GPS devices
3517 // Was a valid number returned?
3523 GList *newlists = NULL;
3524 GList *newtps = NULL;
3529 /* accumulate trackpoint copies in newtps, in reverse order */
3530 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3532 if (count >= points) {
3533 /* flush accumulated trackpoints into new list */
3534 newlists = g_list_append(newlists, g_list_reverse(newtps));
3538 iter = g_list_next(iter);
3541 // If there is a remaining chunk put that into the new split list
3542 // This may well be the whole track if no split points were encountered
3544 newlists = g_list_append(newlists, g_list_reverse(newtps));
3547 /* put lists of trackpoints into tracks */
3550 // Only bother updating if the split results in new tracks
3551 if (g_list_length (newlists) > 1) {
3556 tr = vik_track_new();
3557 tr->visible = track->visible;
3558 tr->trackpoints = (GList *)(iter->data);
3560 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3561 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3563 iter = g_list_next(iter);
3565 // Remove original track and then update the display
3566 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3567 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3569 g_list_free(newlists);
3572 /* end of split/merge routines */
3575 * Similar to trw_layer_enum_item, but this uses a sorted method
3577 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3579 GList **list = (GList**)udata;
3580 //*list = g_list_prepend(*all, key); //unsorted method
3581 // Sort named list alphabetically
3582 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3588 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3590 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3592 // Sort list alphabetically for better presentation
3593 g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3596 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3600 // Get list of items to delete from the user
3601 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3604 _("Delete Selection"),
3605 _("Select tracks to delete"));
3608 // Delete requested tracks
3609 // since specificly requested, IMHO no need for extra confirmation
3610 if ( delete_list ) {
3612 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3613 vik_trw_layer_delete_track(vtl, l->data);
3615 g_list_free(delete_list);
3616 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3623 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3625 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3628 // Sort list alphabetically for better presentation
3629 g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3631 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3635 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3637 // Get list of items to delete from the user
3638 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3641 _("Delete Selection"),
3642 _("Select waypoints to delete"));
3645 // Delete requested waypoints
3646 // since specificly requested, IMHO no need for extra confirmation
3647 if ( delete_list ) {
3649 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3650 vik_trw_layer_delete_waypoint(vtl, l->data);
3652 g_list_free(delete_list);
3653 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3658 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3660 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3662 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3665 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3667 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3668 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3672 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3674 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3679 if (strcmp(newname, sublayer) == 0 )
3682 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3683 if (g_hash_table_lookup( l->waypoints, newname))
3685 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3690 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3691 g_hash_table_steal ( l->waypoints_iters, sublayer );
3693 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3694 highest_wp_number_remove_wp(l, sublayer);
3695 g_hash_table_remove ( l->waypoints, sublayer );
3697 rv = g_strdup(newname);
3699 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3701 highest_wp_number_add_wp(l, rv);
3702 g_hash_table_insert ( l->waypoints, rv, wp );
3703 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3705 /* it hasn't been updated yet so we pass new name */
3706 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3707 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3710 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3713 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3720 if (strcmp(newname, sublayer) == 0)
3723 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3724 if (g_hash_table_lookup( l->tracks, newname))
3726 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3731 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3732 g_hash_table_steal ( l->tracks, sublayer );
3734 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3735 g_hash_table_steal ( l->tracks_iters, sublayer );
3737 rv = g_strdup(newname);
3739 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3741 g_hash_table_insert ( l->tracks, rv, tr );
3742 g_hash_table_insert ( l->tracks_iters, rv, iter );
3744 /* don't forget about current_tp_track_name, update that too */
3745 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3747 l->current_tp_track_name = rv;
3749 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3751 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3752 l->last_tp_track_name = rv;
3754 g_free ( orig_key );
3756 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3757 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3760 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3766 static gboolean is_valid_geocache_name ( gchar *str )
3768 gint len = strlen ( str );
3769 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]));
3772 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3774 gchar *track_name = (gchar *) pass_along[3];
3775 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3776 a_acquire_set_filter_track ( tr, track_name );
3779 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3781 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3782 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3785 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3787 gchar *track_name = (gchar *) pass_along[3];
3788 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3790 gchar *escaped = uri_escape ( tr->comment );
3791 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3792 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3798 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3799 /* viewpoint is now available instead */
3800 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3802 static gpointer pass_along[6];
3804 gboolean rv = FALSE;
3807 pass_along[1] = vlp;
3808 pass_along[2] = GINT_TO_POINTER (subtype);
3809 pass_along[3] = sublayer;
3810 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
3811 pass_along[5] = vvp;
3813 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3817 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3819 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3820 gtk_widget_show ( item );
3822 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3823 VikTrwLayer *vtl = l;
3824 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3825 if (tr && tr->property_dialog)
3826 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3829 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3830 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3831 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3832 gtk_widget_show ( item );
3834 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3835 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3836 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3837 gtk_widget_show ( item );
3839 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3840 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3841 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3842 gtk_widget_show ( item );
3844 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3846 gboolean separator_created = FALSE;
3848 /* could be a right-click using the tool */
3849 if ( vlp != NULL ) {
3850 item = gtk_menu_item_new ();
3851 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3852 gtk_widget_show ( item );
3854 separator_created = TRUE;
3856 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
3857 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3859 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3860 gtk_widget_show ( item );
3863 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3865 if ( !separator_created ) {
3866 item = gtk_menu_item_new ();
3867 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3868 gtk_widget_show ( item );
3869 separator_created = TRUE;
3872 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3873 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3874 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3875 gtk_widget_show ( item );
3878 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
3880 if ( wp && wp->image )
3882 if ( !separator_created ) {
3883 item = gtk_menu_item_new ();
3884 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3885 gtk_widget_show ( item );
3886 separator_created = TRUE;
3889 // Set up image paramater
3890 pass_along[5] = wp->image;
3892 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
3893 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
3894 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
3895 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3896 gtk_widget_show ( item );
3902 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3905 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
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_wp), pass_along );
3908 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3909 gtk_widget_show ( item );
3912 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
3914 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
3915 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3916 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3917 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3918 gtk_widget_show ( item );
3920 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3921 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3922 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3923 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3924 gtk_widget_show ( item );
3926 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
3927 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3929 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3930 gtk_widget_show ( item );
3932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
3933 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3934 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3935 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3936 gtk_widget_show ( item );
3939 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
3943 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
3944 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3946 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3947 gtk_widget_show ( item );
3949 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
3950 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3952 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3953 gtk_widget_show ( item );
3955 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
3956 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3958 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3959 gtk_widget_show ( item );
3962 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3964 GtkWidget *goto_submenu;
3965 item = gtk_menu_item_new ();
3966 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3967 gtk_widget_show ( item );
3969 goto_submenu = gtk_menu_new ();
3970 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
3971 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3972 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3973 gtk_widget_show ( item );
3974 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3976 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
3977 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
3978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3979 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3980 gtk_widget_show ( item );
3982 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3983 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3985 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3986 gtk_widget_show ( item );
3988 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
3989 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
3990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3991 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3992 gtk_widget_show ( item );
3994 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3995 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
3996 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3997 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3998 gtk_widget_show ( item );
4000 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4003 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4004 gtk_widget_show ( item );
4006 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4007 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4009 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4010 gtk_widget_show ( item );
4012 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4013 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4015 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4016 gtk_widget_show ( item );
4018 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4020 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4021 gtk_widget_show ( item );
4023 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4025 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4026 gtk_widget_show ( item );
4028 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4030 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4031 gtk_widget_show ( item );
4033 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4035 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4036 gtk_widget_show ( item );
4038 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4040 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
4042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4043 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4044 gtk_widget_show ( item );
4047 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4048 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("DEM Download/Import", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
4049 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4050 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4051 gtk_widget_show ( item );
4053 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4054 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4055 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
4056 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4057 gtk_widget_show ( item );
4059 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
4060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4062 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4063 gtk_widget_show ( item );
4065 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
4067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4068 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4069 gtk_widget_show ( item );
4071 #ifdef VIK_CONFIG_OPENSTREETMAP
4072 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4074 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4075 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4076 gtk_widget_show ( item );
4079 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
4081 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4082 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4083 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4084 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4085 gtk_widget_show ( item );
4088 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4089 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4090 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4091 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4092 gtk_widget_show ( item );
4094 /* ATM This function is only available via the layers panel, due to needing a vlp */
4096 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4097 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4098 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4100 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4101 gtk_widget_show ( item );
4105 // Only show on viewport popmenu when a trackpoint is selected
4106 if ( ! vlp && l->current_tpl ) {
4108 item = gtk_menu_item_new ();
4109 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4110 gtk_widget_show ( item );
4112 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4113 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4114 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4115 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4116 gtk_widget_show ( item );
4124 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4127 if (!vtl->current_tpl)
4129 if (!vtl->current_tpl->next)
4132 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4133 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4135 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4138 VikTrackpoint *tp_new = vik_trackpoint_new();
4139 struct LatLon ll_current, ll_next;
4140 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4141 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4143 /* main positional interpolation */
4144 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4145 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4147 /* Now other properties that can be interpolated */
4148 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4150 if (tp_current->has_timestamp && tp_next->has_timestamp) {
4151 /* Note here the division is applied to each part, then added
4152 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4153 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4154 tp_new->has_timestamp = TRUE;
4157 if (tp_current->speed != NAN && tp_next->speed != NAN)
4158 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4160 /* TODO - improve interpolation of course, as it may not be correct.
4161 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4162 [similar applies if value is in radians] */
4163 if (tp_current->course != NAN && tp_next->course != NAN)
4164 tp_new->speed = (tp_current->course + tp_next->course)/2;
4166 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4168 /* Insert new point into the trackpoints list after the current TP */
4169 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4170 gint index = g_list_index ( tr->trackpoints, tp_current );
4172 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4177 /* to be called when last_tpl no long exists. */
4178 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4180 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4181 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4182 vtl->last_tpl = NULL;
4183 vtl->last_tp_track_name = NULL;
4186 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4192 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4196 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4198 if ( vtl->current_tpl )
4200 vtl->current_tpl = NULL;
4201 vtl->current_tp_track_name = NULL;
4202 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4206 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4208 g_assert ( vtl->tpwin != NULL );
4209 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4210 trw_layer_cancel_current_tp ( vtl, TRUE );
4212 if ( vtl->current_tpl == NULL )
4215 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4217 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4218 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4220 VikTrack *tr = vik_track_new ();
4221 GList *newglist = g_list_alloc ();
4222 newglist->prev = NULL;
4223 newglist->next = vtl->current_tpl->next;
4224 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4225 tr->trackpoints = newglist;
4227 vtl->current_tpl->next->prev = newglist; /* end old track here */
4228 vtl->current_tpl->next = NULL;
4230 vtl->current_tpl = newglist; /* change tp to first of new track. */
4231 vtl->current_tp_track_name = name;
4233 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4237 vik_trw_layer_add_track ( vtl, name, tr );
4238 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4241 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4243 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4245 g_assert(tr != NULL);
4247 /* can't join with a non-existent trackpoint */
4248 vtl->last_tpl = NULL;
4249 vtl->last_tp_track_name = NULL;
4251 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4253 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4254 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4256 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4258 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4259 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4261 trw_layer_cancel_last_tp ( vtl );
4263 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4264 g_list_free_1 ( vtl->current_tpl );
4265 vtl->current_tpl = new_tpl;
4266 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4270 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4271 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4272 g_list_free_1 ( vtl->current_tpl );
4273 trw_layer_cancel_current_tp ( vtl, FALSE );
4276 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4278 vtl->last_tpl = vtl->current_tpl;
4279 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4280 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4282 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4284 vtl->last_tpl = vtl->current_tpl;
4285 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4286 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4288 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4290 // Check tracks exist and are different before joining
4291 if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4294 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4295 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4297 VikTrack *tr_first = tr1, *tr_last = tr2;
4301 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4302 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4303 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4304 vik_track_reverse ( tr1 );
4305 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4310 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4312 if ( tr_last->trackpoints ) /* deleting this part here joins them into 1 segmented track. useful but there's no place in the UI for this feature. segments should be deprecated anyway. */
4313 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4314 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4315 tr2->trackpoints = NULL;
4317 tmp = vtl->current_tp_track_name;
4319 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4320 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4322 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4323 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4324 vik_trw_layer_delete_track ( vtl, tmp );
4326 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4327 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4329 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4331 trw_layer_insert_tp_after_current_tp ( vtl );
4332 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4334 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4335 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4338 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4342 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4343 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4344 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4345 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4346 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4348 if ( vtl->current_tpl )
4349 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4350 /* set layer name and TP data */
4353 /***************************************************************************
4355 ***************************************************************************/
4357 /*** Utility data structures and functions ****/
4361 gint closest_x, closest_y;
4362 gchar *closest_wp_name;
4363 VikWaypoint *closest_wp;
4369 gint closest_x, closest_y;
4370 gchar *closest_track_name;
4371 VikTrackpoint *closest_tp;
4376 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4382 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4384 // If waypoint has an image then use the image size to select
4386 gint slackx, slacky;
4387 slackx = wp->image_width / 2;
4388 slacky = wp->image_height / 2;
4390 if ( x <= params->x + slackx && x >= params->x - slackx
4391 && y <= params->y + slacky && y >= params->y - slacky ) {
4392 params->closest_wp_name = name;
4393 params->closest_wp = wp;
4394 params->closest_x = x;
4395 params->closest_y = y;
4398 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4399 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
4400 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4402 params->closest_wp_name = name;
4403 params->closest_wp = wp;
4404 params->closest_x = x;
4405 params->closest_y = y;
4409 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4411 GList *tpl = t->trackpoints;
4420 tp = VIK_TRACKPOINT(tpl->data);
4422 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4424 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4425 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
4426 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4428 params->closest_track_name = name;
4429 params->closest_tp = tp;
4430 params->closest_tpl = tpl;
4431 params->closest_x = x;
4432 params->closest_y = y;
4438 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4440 TPSearchParams params;
4444 params.closest_track_name = NULL;
4445 params.closest_tp = NULL;
4446 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4447 return params.closest_tp;
4450 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4452 WPSearchParams params;
4456 params.closest_wp = NULL;
4457 params.closest_wp_name = NULL;
4458 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4459 return params.closest_wp;
4463 // Some forward declarations
4464 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4465 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4466 static void marker_end_move ( tool_ed_t *t );
4469 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4473 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4475 // Here always allow snapping back to the original location
4476 // this is useful when one decides not to move the thing afterall
4477 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4480 if ( event->state & GDK_CONTROL_MASK )
4482 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4484 new_coord = tp->coord;
4488 if ( event->state & GDK_SHIFT_MASK )
4490 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4492 new_coord = wp->coord;
4496 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4498 marker_moveto ( t, x, y );
4505 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4507 if ( t->holding && event->button == 1 )
4510 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4513 if ( event->state & GDK_CONTROL_MASK )
4515 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4517 new_coord = tp->coord;
4521 if ( event->state & GDK_SHIFT_MASK )
4523 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4525 new_coord = wp->coord;
4528 marker_end_move ( t );
4530 // Determine if working on a waypoint or a trackpoint
4531 if ( t->is_waypoint )
4532 vtl->current_wp->coord = new_coord;
4534 if ( vtl->current_tpl ) {
4535 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4538 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4540 // Don't really know what this is for but seems like it might be handy...
4541 /* can't join with itself! */
4542 trw_layer_cancel_last_tp ( vtl );
4547 vtl->current_wp = NULL;
4548 vtl->current_wp_name = NULL;
4549 trw_layer_cancel_current_tp ( vtl, FALSE );
4551 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4558 Returns true if a waypoint or track is found near the requested event position for this particular layer
4559 The item found is automatically selected
4560 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4562 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4564 if ( event->button != 1 )
4567 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4570 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4573 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4575 if (vtl->waypoints_visible) {
4576 WPSearchParams wp_params;
4577 wp_params.vvp = vvp;
4578 wp_params.x = event->x;
4579 wp_params.y = event->y;
4580 wp_params.closest_wp_name = NULL;
4581 wp_params.closest_wp = NULL;
4583 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4585 if ( wp_params.closest_wp ) {
4588 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4590 // Too easy to move it so must be holding shift to start immediately moving it
4591 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
4592 if ( event->state & GDK_SHIFT_MASK ||
4593 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
4594 // Put into 'move buffer'
4595 // NB vvp & vw already set in tet
4596 tet->vtl = (gpointer)vtl;
4597 tet->is_waypoint = TRUE;
4599 marker_begin_move (tet, event->x, event->y);
4602 vtl->current_wp = wp_params.closest_wp;
4603 vtl->current_wp_name = wp_params.closest_wp_name;
4605 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4611 if (vtl->tracks_visible) {
4612 TPSearchParams tp_params;
4613 tp_params.vvp = vvp;
4614 tp_params.x = event->x;
4615 tp_params.y = event->y;
4616 tp_params.closest_track_name = NULL;
4617 tp_params.closest_tp = NULL;
4619 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4621 if ( tp_params.closest_tp ) {
4623 // Always select + highlight the track
4624 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4626 tet->is_waypoint = FALSE;
4628 // Select the Trackpoint
4629 // Can move it immediately when control held or it's the previously selected tp
4630 if ( event->state & GDK_CONTROL_MASK ||
4631 vtl->current_tpl == tp_params.closest_tpl ) {
4632 // Put into 'move buffer'
4633 // NB vvp & vw already set in tet
4634 tet->vtl = (gpointer)vtl;
4635 marker_begin_move (tet, event->x, event->y);
4638 vtl->current_tpl = tp_params.closest_tpl;
4639 vtl->current_tp_track_name = tp_params.closest_track_name;
4641 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
4644 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4646 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4651 /* these aren't the droids you're looking for */
4652 vtl->current_wp = NULL;
4653 vtl->current_wp_name = NULL;
4654 trw_layer_cancel_current_tp ( vtl, FALSE );
4657 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
4662 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4664 if ( event->button != 3 )
4667 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4670 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4673 /* Post menu for the currently selected item */
4675 /* See if a track is selected */
4676 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4677 if ( track && track->visible ) {
4679 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4681 if ( vtl->track_right_click_menu )
4682 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4684 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4686 trw_layer_sublayer_add_menu_items ( vtl,
4687 vtl->track_right_click_menu,
4689 VIK_TRW_LAYER_SUBLAYER_TRACK,
4690 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4691 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4694 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4700 /* See if a waypoint is selected */
4701 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4702 if ( waypoint && waypoint->visible ) {
4703 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4705 if ( vtl->wp_right_click_menu )
4706 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4708 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4709 trw_layer_sublayer_add_menu_items ( vtl,
4710 vtl->wp_right_click_menu,
4712 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4713 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4714 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4716 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4725 /* background drawing hook, to be passed the viewport */
4726 static gboolean tool_sync_done = TRUE;
4728 static gboolean tool_sync(gpointer data)
4730 VikViewport *vvp = data;
4731 gdk_threads_enter();
4732 vik_viewport_sync(vvp);
4733 tool_sync_done = TRUE;
4734 gdk_threads_leave();
4738 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4741 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4742 gdk_gc_set_function ( t->gc, GDK_INVERT );
4743 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4744 vik_viewport_sync(t->vvp);
4749 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4751 VikViewport *vvp = t->vvp;
4752 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4753 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4757 if (tool_sync_done) {
4758 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4759 tool_sync_done = FALSE;
4763 static void marker_end_move ( tool_ed_t *t )
4765 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4766 g_object_unref ( t->gc );
4770 /*** Edit waypoint ****/
4772 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4774 tool_ed_t *t = g_new(tool_ed_t, 1);
4780 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4782 WPSearchParams params;
4783 tool_ed_t *t = data;
4784 VikViewport *vvp = t->vvp;
4786 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4793 if ( !vtl->vl.visible || !vtl->waypoints_visible )
4796 if ( vtl->current_wp && vtl->current_wp->visible )
4798 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
4800 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
4802 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
4803 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
4805 if ( event->button == 3 )
4806 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4808 marker_begin_move(t, event->x, event->y);
4815 params.x = event->x;
4816 params.y = event->y;
4817 params.closest_wp_name = NULL;
4818 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4819 params.closest_wp = NULL;
4820 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4821 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
4823 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
4824 marker_begin_move(t, event->x, event->y);
4825 g_critical("shouldn't be here");
4828 else if ( params.closest_wp )
4830 if ( event->button == 3 )
4831 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4833 vtl->waypoint_rightclick = FALSE;
4835 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
4837 vtl->current_wp = params.closest_wp;
4838 vtl->current_wp_name = params.closest_wp_name;
4840 /* could make it so don't update if old WP is off screen and new is null but oh well */
4841 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4845 vtl->current_wp = NULL;
4846 vtl->current_wp_name = NULL;
4847 vtl->waypoint_rightclick = FALSE;
4848 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4852 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4854 tool_ed_t *t = data;
4855 VikViewport *vvp = t->vvp;
4857 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4862 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4865 if ( event->state & GDK_CONTROL_MASK )
4867 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4869 new_coord = tp->coord;
4873 if ( event->state & GDK_SHIFT_MASK )
4875 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4876 if ( wp && wp != vtl->current_wp )
4877 new_coord = wp->coord;
4882 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4884 marker_moveto ( t, x, y );
4891 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4893 tool_ed_t *t = data;
4894 VikViewport *vvp = t->vvp;
4896 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4899 if ( t->holding && event->button == 1 )
4902 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4905 if ( event->state & GDK_CONTROL_MASK )
4907 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4909 new_coord = tp->coord;
4913 if ( event->state & GDK_SHIFT_MASK )
4915 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4916 if ( wp && wp != vtl->current_wp )
4917 new_coord = wp->coord;
4920 marker_end_move ( t );
4922 vtl->current_wp->coord = new_coord;
4923 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4926 /* PUT IN RIGHT PLACE!!! */
4927 if ( event->button == 3 && vtl->waypoint_rightclick )
4929 if ( vtl->wp_right_click_menu )
4930 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
4931 if ( vtl->current_wp ) {
4932 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4933 trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_name, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ), vvp );
4934 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4936 vtl->waypoint_rightclick = FALSE;
4941 /**** Begin track ***/
4942 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4947 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4949 vtl->current_track = NULL;
4950 return tool_new_track_click ( vtl, event, vvp );
4953 /*** New track ****/
4955 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4963 gint x1,y1,x2,y2,x3,y3;
4965 } new_track_move_passalong_t;
4967 /* sync and undraw, but only when we have time */
4968 static gboolean ct_sync ( gpointer passalong )
4970 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4972 vik_viewport_sync ( p->vvp );
4973 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4974 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4975 vik_viewport_draw_string (p->vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), p->vtl->current_track_gc, p->x3, p->y3, p->str);
4976 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4978 g_free ( (gpointer) p->str ) ;
4979 p->vtl->ct_sync_done = TRUE;
4984 static const gchar* distance_string (gdouble distance)
4988 /* draw label with distance */
4989 vik_units_distance_t dist_units = a_vik_get_units_distance ();
4990 switch (dist_units) {
4991 case VIK_UNITS_DISTANCE_MILES:
4992 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
4993 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
4994 } else if (distance < 1609.4) {
4995 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
4997 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5001 // VIK_UNITS_DISTANCE_KILOMETRES
5002 if (distance >= 1000 && distance < 100000) {
5003 g_sprintf(str, "%3.2f km", distance/1000.0);
5004 } else if (distance < 1000) {
5005 g_sprintf(str, "%d m", (int)distance);
5007 g_sprintf(str, "%d km", (int)distance/1000);
5011 return g_strdup (str);
5015 * Actually set the message in statusbar
5017 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5019 // Only show elevation data when track has some elevation properties
5020 gchar str_gain_loss[64];
5021 str_gain_loss[0] = '\0';
5023 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5024 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5025 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5027 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5030 // Write with full gain/loss information
5031 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5032 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5037 * Figure out what information should be set in the statusbar and then write it
5039 static void update_statusbar ( VikTrwLayer *vtl )
5041 // Get elevation data
5042 gdouble elev_gain, elev_loss;
5043 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5045 /* Find out actual distance of current track */
5046 gdouble distance = vik_track_get_length (vtl->current_track);
5047 const gchar *str = distance_string (distance);
5049 statusbar_write (str, elev_gain, elev_loss, vtl);
5051 g_free ((gpointer)str);
5055 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5057 /* if we haven't sync'ed yet, we don't have time to do more. */
5058 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5059 GList *iter = vtl->current_track->trackpoints;
5060 new_track_move_passalong_t *passalong;
5063 while ( iter->next )
5065 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5066 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5067 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5069 /* Find out actual distance of current track */
5070 gdouble distance = vik_track_get_length (vtl->current_track);
5072 // Now add distance to where the pointer is //
5075 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5076 vik_coord_to_latlon ( &coord, &ll );
5077 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5079 // Get elevation data
5080 gdouble elev_gain, elev_loss;
5081 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5083 // Adjust elevation data (if available) for the current pointer position
5085 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5086 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5087 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5088 // Adjust elevation of last track point
5089 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5091 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5094 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5098 const gchar *str = distance_string (distance);
5100 /* offset from cursor a bit */
5103 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5104 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5106 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5108 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5109 passalong->vtl = vtl;
5110 passalong->vvp = vvp;
5113 passalong->x2 = event->x;
5114 passalong->y2 = event->y;
5117 passalong->str = str;
5119 // Update statusbar with full gain/loss information
5120 statusbar_write (str, elev_gain, elev_loss, vtl);
5122 /* this will sync and undraw when we have time to */
5123 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5124 vtl->ct_sync_done = FALSE;
5125 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5127 return VIK_LAYER_TOOL_ACK;
5130 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5132 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5133 vtl->current_track = NULL;
5134 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5136 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5138 if ( vtl->current_track->trackpoints )
5140 GList *last = g_list_last(vtl->current_track->trackpoints);
5141 g_free ( last->data );
5142 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5145 update_statusbar ( vtl );
5147 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5153 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5157 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5160 if ( event->button == 3 && vtl->current_track )
5163 if ( vtl->current_track->trackpoints )
5165 GList *last = g_list_last(vtl->current_track->trackpoints);
5166 g_free ( last->data );
5167 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5169 update_statusbar ( vtl );
5171 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5175 if ( event->type == GDK_2BUTTON_PRESS )
5177 /* subtract last (duplicate from double click) tp then end */
5178 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5180 GList *last = g_list_last(vtl->current_track->trackpoints);
5181 g_free ( last->data );
5182 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5183 /* undo last, then end */
5184 vtl->current_track = NULL;
5186 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5190 if ( ! vtl->current_track )
5192 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5193 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5195 vtl->current_track = vik_track_new();
5196 vtl->current_track->visible = TRUE;
5197 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5199 /* incase it was created by begin track */
5200 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5205 tp = vik_trackpoint_new();
5206 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5208 /* snap to other TP */
5209 if ( event->state & GDK_CONTROL_MASK )
5211 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5213 tp->coord = other_tp->coord;
5216 tp->newsegment = FALSE;
5217 tp->has_timestamp = FALSE;
5219 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5220 /* Auto attempt to get elevation from DEM data (if it's available) */
5221 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5223 vtl->ct_x1 = vtl->ct_x2;
5224 vtl->ct_y1 = vtl->ct_y2;
5225 vtl->ct_x2 = event->x;
5226 vtl->ct_y2 = event->y;
5228 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5232 /*** New waypoint ****/
5234 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5239 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5242 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5244 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5245 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5246 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5251 /*** Edit trackpoint ****/
5253 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5255 tool_ed_t *t = g_new(tool_ed_t, 1);
5261 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5263 tool_ed_t *t = data;
5264 VikViewport *vvp = t->vvp;
5265 TPSearchParams params;
5266 /* OUTDATED DOCUMENTATION:
5267 find 5 pixel range on each side. then put these UTM, and a pointer
5268 to the winning track name (and maybe the winning track itself), and a
5269 pointer to the winning trackpoint, inside an array or struct. pass
5270 this along, do a foreach on the tracks which will do a foreach on the
5273 params.x = event->x;
5274 params.y = event->y;
5275 params.closest_track_name = NULL;
5276 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5277 params.closest_tp = NULL;
5279 if ( event->button != 1 )
5282 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5285 if ( !vtl->vl.visible || !vtl->tracks_visible )
5288 if ( vtl->current_tpl )
5290 /* 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.) */
5291 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5292 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5294 g_assert ( current_tr );
5296 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5298 if ( current_tr->visible &&
5299 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5300 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5301 marker_begin_move ( t, event->x, event->y );
5305 vtl->last_tpl = vtl->current_tpl;
5306 vtl->last_tp_track_name = vtl->current_tp_track_name;
5309 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5311 if ( params.closest_tp )
5313 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5314 vtl->current_tpl = params.closest_tpl;
5315 vtl->current_tp_track_name = params.closest_track_name;
5316 trw_layer_tpwin_init ( vtl );
5317 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5318 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5322 /* these aren't the droids you're looking for */
5326 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5328 tool_ed_t *t = data;
5329 VikViewport *vvp = t->vvp;
5331 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5337 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5340 if ( event->state & GDK_CONTROL_MASK )
5342 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5343 if ( tp && tp != vtl->current_tpl->data )
5344 new_coord = tp->coord;
5346 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5349 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5350 marker_moveto ( t, x, y );
5358 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5360 tool_ed_t *t = data;
5361 VikViewport *vvp = t->vvp;
5363 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5365 if ( event->button != 1)
5370 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5373 if ( event->state & GDK_CONTROL_MASK )
5375 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5376 if ( tp && tp != vtl->current_tpl->data )
5377 new_coord = tp->coord;
5380 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5382 marker_end_move ( t );
5384 /* diff dist is diff from orig */
5386 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5387 /* can't join with itself! */
5388 trw_layer_cancel_last_tp ( vtl );
5390 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5397 /*** Route Finder ***/
5398 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5403 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5406 if ( !vtl ) return FALSE;
5407 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5408 if ( event->button == 3 && vtl->route_finder_current_track ) {
5410 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5412 vtl->route_finder_coord = *new_end;
5414 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5415 /* remove last ' to:...' */
5416 if ( vtl->route_finder_current_track->comment ) {
5417 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5418 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5419 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5420 last_to - vtl->route_finder_current_track->comment - 1);
5421 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5426 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5427 struct LatLon start, end;
5428 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5429 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5432 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5433 vik_coord_to_latlon ( &(tmp), &end );
5434 vtl->route_finder_coord = tmp; /* for continuations */
5436 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5437 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5438 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
5440 vtl->route_finder_check_added_track = TRUE;
5441 vtl->route_finder_started = FALSE;
5444 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5445 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5446 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5447 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5448 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5449 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5452 /* see if anything was done -- a track was added or appended to */
5453 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5456 tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5459 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5461 vtl->route_finder_current_track = tr;
5463 g_free ( vtl->route_finder_added_track_name );
5464 vtl->route_finder_added_track_name = NULL;
5465 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5466 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5467 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5468 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5470 vtl->route_finder_check_added_track = FALSE;
5471 vtl->route_finder_append = FALSE;
5473 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5475 vtl->route_finder_started = TRUE;
5476 vtl->route_finder_coord = tmp;
5477 vtl->route_finder_current_track = NULL;
5482 /*** Show picture ****/
5484 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5489 /* Params are: vvp, event, last match found or NULL */
5490 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
5492 if ( wp->image && wp->visible )
5494 gint x, y, slackx, slacky;
5495 GdkEventButton *event = (GdkEventButton *) params[1];
5497 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5498 slackx = wp->image_width / 2;
5499 slacky = wp->image_height / 2;
5500 if ( x <= event->x + slackx && x >= event->x - slackx
5501 && y <= event->y + slacky && y >= event->y - slacky )
5503 params[2] = wp->image; /* we've found a match. however continue searching
5504 * since we want to find the last match -- that
5505 * is, the match that was drawn last. */
5510 static void trw_layer_show_picture ( gpointer pass_along[6] )
5512 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5514 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5517 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5518 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
5519 g_free ( quoted_file );
5520 if ( ! g_spawn_command_line_async ( cmd, &err ) )
5522 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch eog to open file.") );
5523 g_error_free ( err );
5526 #endif /* WINDOWS */
5529 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5531 gpointer params[3] = { vvp, event, NULL };
5532 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5534 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5537 static gpointer pass_along[6];
5538 pass_along[0] = vtl;
5539 pass_along[5] = params[2];
5540 trw_layer_show_picture ( pass_along );
5541 return TRUE; /* found a match */
5544 return FALSE; /* go through other layers, searching for a match */
5547 /***************************************************************************
5549 ***************************************************************************/
5555 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5557 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5558 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5561 /* Structure for thumbnail creating data used in the background thread */
5563 VikTrwLayer *vtl; // Layer needed for redrawing
5564 GSList *pics; // Image list
5565 } thumbnail_create_thread_data;
5567 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5569 guint total = g_slist_length(tctd->pics), done = 0;
5570 while ( tctd->pics )
5572 a_thumbnails_create ( (gchar *) tctd->pics->data );
5573 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5575 return -1; /* Abort thread */
5577 tctd->pics = tctd->pics->next;
5580 // Redraw to show the thumbnails as they are now created
5581 if ( IS_VIK_LAYER(tctd->vtl) )
5582 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
5587 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5589 while ( tctd->pics )
5591 g_free ( tctd->pics->data );
5592 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5597 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5599 if ( ! vtl->has_verified_thumbnails )
5601 GSList *pics = NULL;
5602 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5605 gint len = g_slist_length ( pics );
5606 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5607 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5610 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5612 (vik_thr_func) create_thumbnails_thread,
5614 (vik_thr_free_func) thumbnail_create_thread_free,
5622 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5624 return vtl->coord_mode;
5629 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5631 vik_coord_convert ( &(wp->coord), *dest_mode );
5634 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5636 vik_track_convert ( tr, *dest_mode );
5639 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5641 if ( vtl->coord_mode != dest_mode )
5643 vtl->coord_mode = dest_mode;
5644 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5645 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5649 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5651 return g_hash_table_lookup ( vtl->waypoints, name );
5654 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5656 return g_hash_table_lookup ( vtl->tracks, name );
5659 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5661 vtl->menu_selection = selection;
5664 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5666 return (vtl->menu_selection);
5669 /* ----------- Downloading maps along tracks --------------- */
5671 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5673 /* TODO: calculating based on current size of viewport */
5674 const gdouble w_at_zoom_0_125 = 0.0013;
5675 const gdouble h_at_zoom_0_125 = 0.0011;
5676 gdouble zoom_factor = zoom_level/0.125;
5678 wh->lat = h_at_zoom_0_125 * zoom_factor;
5679 wh->lon = w_at_zoom_0_125 * zoom_factor;
5681 return 0; /* all OK */
5684 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5686 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5687 (dist->lat >= ABS(to->north_south - from->north_south)))
5690 VikCoord *coord = g_malloc(sizeof(VikCoord));
5691 coord->mode = VIK_COORD_LATLON;
5693 if (ABS(gradient) < 1) {
5694 if (from->east_west > to->east_west)
5695 coord->east_west = from->east_west - dist->lon;
5697 coord->east_west = from->east_west + dist->lon;
5698 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5700 if (from->north_south > to->north_south)
5701 coord->north_south = from->north_south - dist->lat;
5703 coord->north_south = from->north_south + dist->lat;
5704 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5710 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5712 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5713 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5715 VikCoord *next = from;
5717 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5719 list = g_list_prepend(list, next);
5725 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5727 typedef struct _Rect {
5732 #define GLRECT(iter) ((Rect *)((iter)->data))
5735 GList *rects_to_download = NULL;
5738 if (get_download_area_width(vvp, zoom_level, &wh))
5741 GList *iter = tr->trackpoints;
5745 gboolean new_map = TRUE;
5746 VikCoord *cur_coord, tl, br;
5749 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5751 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5752 rect = g_malloc(sizeof(Rect));
5755 rect->center = *cur_coord;
5756 rects_to_download = g_list_prepend(rects_to_download, rect);
5761 gboolean found = FALSE;
5762 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5763 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
5774 GList *fillins = NULL;
5775 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
5776 /* seems that ATM the function get_next_coord works only for LATLON */
5777 if ( cur_coord->mode == VIK_COORD_LATLON ) {
5778 /* fill-ins for far apart points */
5779 GList *cur_rect, *next_rect;
5780 for (cur_rect = rects_to_download;
5781 (next_rect = cur_rect->next) != NULL;
5782 cur_rect = cur_rect->next) {
5783 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
5784 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
5785 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
5789 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
5792 GList *iter = fillins;
5794 cur_coord = (VikCoord *)(iter->data);
5795 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5796 rect = g_malloc(sizeof(Rect));
5799 rect->center = *cur_coord;
5800 rects_to_download = g_list_prepend(rects_to_download, rect);
5805 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5806 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
5810 for (iter = fillins; iter; iter = iter->next)
5812 g_list_free(fillins);
5814 if (rects_to_download) {
5815 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
5816 g_free(rect_iter->data);
5817 g_list_free(rects_to_download);
5821 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
5824 gint selected_map, default_map;
5825 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
5826 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
5827 gint selected_zoom, default_zoom;
5831 VikTrwLayer *vtl = pass_along[0];
5832 VikLayersPanel *vlp = pass_along[1];
5833 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5834 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
5836 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
5837 int num_maps = g_list_length(vmls);
5840 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
5844 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
5845 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
5847 gchar **np = map_names;
5848 VikMapsLayer **lp = map_layers;
5849 for (i = 0; i < num_maps; i++) {
5850 gboolean dup = FALSE;
5851 vml = (VikMapsLayer *)(vmls->data);
5852 for (j = 0; j < i; j++) { /* no duplicate allowed */
5853 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
5860 *np++ = vik_maps_layer_get_map_label(vml);
5866 num_maps = lp - map_layers;
5868 for (default_map = 0; default_map < num_maps; default_map++) {
5869 /* TODO: check for parent layer's visibility */
5870 if (VIK_LAYER(map_layers[default_map])->visible)
5873 default_map = (default_map == num_maps) ? 0 : default_map;
5875 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
5876 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
5877 if (cur_zoom == zoom_vals[default_zoom])
5880 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
5882 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
5885 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
5888 for (i = 0; i < num_maps; i++)
5889 g_free(map_names[i]);
5897 /**** lowest waypoint number calculation ***/
5898 static gint highest_wp_number_name_to_number(const gchar *name) {
5899 if ( strlen(name) == 3 ) {
5901 if ( n < 100 && name[0] != '0' )
5903 if ( n < 10 && name[0] != '0' )
5911 static void highest_wp_number_reset(VikTrwLayer *vtl)
5913 vtl->highest_wp_number = -1;
5916 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
5918 /* if is bigger that top, add it */
5919 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
5920 if ( new_wp_num > vtl->highest_wp_number )
5921 vtl->highest_wp_number = new_wp_num;
5924 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
5926 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
5927 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
5928 if ( vtl->highest_wp_number == old_wp_num ) {
5930 vtl->highest_wp_number --;
5932 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5933 /* search down until we find something that *does* exist */
5935 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
5936 vtl->highest_wp_number --;
5937 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5942 /* get lowest unused number */
5943 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
5946 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
5948 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
5949 return g_strdup(buf);