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 #ifdef VIK_CONFIG_GEOTAG
40 #include "viktrwlayer_geotag.h"
41 #include "geotag_exif.h"
43 #include "garminsymbols.h"
44 #include "thumbnails.h"
45 #include "background.h"
50 #include "geonamessearch.h"
51 #ifdef VIK_CONFIG_OPENSTREETMAP
52 #include "osm-traces.h"
55 #include "datasources.h"
58 #include "icons/icons.h"
72 #include <gdk/gdkkeysyms.h>
74 #include <glib/gstdio.h>
75 #include <glib/gi18n.h>
77 /* Relax some dependencies */
78 #if ! GLIB_CHECK_VERSION(2,12,0)
79 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
80 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
83 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
84 #define VIK_TRW_LAYER_TRACK_GC 13
85 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
86 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
87 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
88 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
90 #define DRAWMODE_BY_TRACK 0
91 #define DRAWMODE_BY_VELOCITY 1
92 #define DRAWMODE_ALL_BLACK 2
97 /* this is how it knows when you click if you are clicking close to a trackpoint. */
98 #define TRACKPOINT_SIZE_APPROX 5
99 #define WAYPOINT_SIZE_APPROX 5
101 #define MIN_STOP_LENGTH 15
102 #define MAX_STOP_LENGTH 86400
103 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
104 /* this is multiplied by user-inputted value from 1-100. */
106 VIK_TRW_LAYER_SUBLAYER_TRACKS,
107 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
108 VIK_TRW_LAYER_SUBLAYER_TRACK,
109 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
112 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
114 struct _VikTrwLayer {
117 GHashTable *tracks_iters;
118 GHashTable *waypoints_iters;
119 GHashTable *waypoints;
120 GtkTreeIter waypoints_iter, tracks_iter;
121 gboolean tracks_visible, waypoints_visible;
124 guint8 drawelevation;
125 guint8 elevation_factor;
129 guint8 line_thickness;
130 guint8 bg_line_thickness;
134 gboolean wp_draw_symbols;
136 gdouble velocity_min, velocity_max;
138 guint16 track_gc_iter;
139 GdkGC *current_track_gc;
142 GdkGC *waypoint_text_gc;
143 GdkGC *waypoint_bg_gc;
144 GdkFont *waypoint_font;
145 VikTrack *current_track;
146 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
147 gboolean ct_sync_done;
150 VikCoordMode coord_mode;
152 /* wp editing tool */
153 VikWaypoint *current_wp;
154 gchar *current_wp_name;
156 gboolean waypoint_rightclick;
158 /* track editing tool */
160 gchar *current_tp_track_name;
161 VikTrwLayerTpwin *tpwin;
163 /* weird hack for joining tracks */
165 gchar *last_tp_track_name;
167 /* track editing tool -- more specifically, moving tps */
170 /* route finder tool */
171 gboolean route_finder_started;
172 VikCoord route_finder_coord;
173 gboolean route_finder_check_added_track;
174 gchar *route_finder_added_track_name;
175 VikTrack *route_finder_current_track;
176 gboolean route_finder_append;
183 guint16 image_cache_size;
185 /* for waypoint text */
186 PangoLayout *wplabellayout;
188 gboolean has_verified_thumbnails;
190 GtkMenu *wp_right_click_menu;
191 GtkMenu *track_right_click_menu;
194 VikStdLayerMenuItem menu_selection;
196 gint highest_wp_number;
199 /* A caached waypoint image. */
202 gchar *image; /* filename */
205 struct DrawingParams {
209 guint16 width, height;
210 const VikCoord *center;
212 gboolean one_zone, lat_lon;
213 gdouble ce1, ce2, cn1, cn2;
216 static void trw_layer_delete_item ( gpointer pass_along[6] );
217 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
218 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
220 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
221 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
222 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
224 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
225 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
227 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
228 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
229 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
231 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
232 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
233 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
234 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
235 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
236 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
237 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
238 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
239 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
240 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
241 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
242 static void trw_layer_reverse ( gpointer pass_along[6] );
243 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
244 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
245 static void trw_layer_show_picture ( gpointer pass_along[6] );
247 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
248 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
249 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
250 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
251 static void trw_layer_new_wp ( gpointer lav[2] );
252 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
253 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
254 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
255 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
256 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
257 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
258 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
259 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
260 #ifdef VIK_CONFIG_GEOTAG
261 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
262 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
263 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
264 static void trw_layer_geotagging ( gpointer lav[2] );
266 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
267 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
268 #ifdef VIK_CONFIG_OPENSTREETMAP
269 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
271 #ifdef VIK_CONFIG_GEOCACHES
272 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
274 #ifdef VIK_CONFIG_GEOTAG
275 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
279 static void trw_layer_properties_item ( gpointer pass_along[6] );
280 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
281 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
283 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
284 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
285 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
287 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
288 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
289 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
290 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
291 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
293 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
294 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
295 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
296 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
297 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
298 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
299 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
300 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
301 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
302 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
303 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
304 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
305 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
306 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
307 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
308 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
309 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
310 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
311 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
312 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
315 static void cached_pixbuf_free ( CachedPixbuf *cp );
316 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
318 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
319 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
321 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
322 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
323 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
325 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
326 static void highest_wp_number_reset(VikTrwLayer *vtl);
327 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
328 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
331 static VikToolInterface trw_layer_tools[] = {
332 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
333 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
335 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
336 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
337 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
339 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
340 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
342 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
343 (VikToolMouseFunc) tool_edit_waypoint_click,
344 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
345 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
347 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
348 (VikToolMouseFunc) tool_edit_trackpoint_click,
349 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
350 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
352 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
353 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
355 { N_("Route Finder"), (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
356 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
358 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
360 /****** PARAMETERS ******/
362 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
363 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
365 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
366 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
369 static VikLayerParamScale params_scales[] = {
370 /* min max step digits */
371 { 1, 10, 1, 0 }, /* line_thickness */
372 { 0.0, 99.0, 1, 2 }, /* velocity_min */
373 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
374 /* 5 * step == how much to turn */
375 { 16, 128, 3.2, 0 }, /* image_size */
376 { 0, 255, 5, 0 }, /* image alpha */
377 { 5, 500, 5, 0 }, /* image cache_size */
378 { 0, 8, 1, 0 }, /* image cache_size */
379 { 1, 64, 1, 0 }, /* wpsize */
380 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
381 { 1, 100, 1, 0 }, /* stop_length */
384 VikLayerParam trw_layer_params[] = {
385 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
386 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
388 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
389 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
390 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
391 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
392 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
394 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
395 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
397 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
398 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
399 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
400 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
401 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
403 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
404 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
405 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
406 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
407 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
408 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
409 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
410 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
412 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
413 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
414 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
415 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
418 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 };
421 *** 1) Add to trw_layer_params and enumeration
422 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
425 /****** END PARAMETERS ******/
427 static VikTrwLayer* trw_layer_new ( gint drawmode );
428 /* Layer Interface function definitions */
429 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
430 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
431 static void trw_layer_free ( VikTrwLayer *trwlayer );
432 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
433 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
434 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
435 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
436 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
437 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
438 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
439 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
440 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
441 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
442 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
443 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
444 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
445 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
446 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
447 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
448 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
449 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
450 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
451 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
452 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
453 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
454 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
455 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
456 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
457 /* End Layer Interface function definitions */
459 VikLayerInterface vik_trw_layer_interface = {
464 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
468 params_groups, /* params_groups */
469 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
473 (VikLayerFuncCreate) trw_layer_create,
474 (VikLayerFuncRealize) trw_layer_realize,
475 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
476 (VikLayerFuncFree) trw_layer_free,
478 (VikLayerFuncProperties) NULL,
479 (VikLayerFuncDraw) trw_layer_draw,
480 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
482 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
483 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
485 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
486 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
488 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
489 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
490 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
491 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
492 (VikLayerFuncLayerSelected) trw_layer_selected,
494 (VikLayerFuncMarshall) trw_layer_marshall,
495 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
497 (VikLayerFuncSetParam) trw_layer_set_param,
498 (VikLayerFuncGetParam) trw_layer_get_param,
500 (VikLayerFuncReadFileData) a_gpspoint_read_file,
501 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
503 (VikLayerFuncDeleteItem) trw_layer_del_item,
504 (VikLayerFuncCutItem) trw_layer_cut_item,
505 (VikLayerFuncCopyItem) trw_layer_copy_item,
506 (VikLayerFuncPasteItem) trw_layer_paste_item,
507 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
509 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
511 (VikLayerFuncSelectClick) trw_layer_select_click,
512 (VikLayerFuncSelectMove) trw_layer_select_move,
513 (VikLayerFuncSelectRelease) trw_layer_select_release,
514 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
517 /* for copy & paste (I think?) */
525 GType vik_trw_layer_get_type ()
527 static GType vtl_type = 0;
531 static const GTypeInfo vtl_info =
533 sizeof (VikTrwLayerClass),
534 NULL, /* base_init */
535 NULL, /* base_finalize */
536 NULL, /* class init */
537 NULL, /* class_finalize */
538 NULL, /* class_data */
539 sizeof (VikTrwLayer),
541 NULL /* instance init */
543 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
549 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
551 static gpointer pass_along[6];
557 pass_along[1] = NULL;
558 pass_along[2] = GINT_TO_POINTER (subtype);
559 pass_along[3] = sublayer;
560 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
561 pass_along[5] = NULL;
563 trw_layer_delete_item ( pass_along );
566 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
568 static gpointer pass_along[6];
574 pass_along[1] = NULL;
575 pass_along[2] = GINT_TO_POINTER (subtype);
576 pass_along[3] = sublayer;
577 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
578 pass_along[5] = NULL;
580 trw_layer_copy_item_cb(pass_along);
581 trw_layer_cut_item_cb(pass_along);
584 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
586 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
587 gint subtype = GPOINTER_TO_INT (pass_along[2]);
588 gpointer * sublayer = pass_along[3];
592 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
595 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
596 subtype, len, (const gchar*) sublayer, data);
600 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
602 trw_layer_copy_item_cb(pass_along);
603 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
604 trw_layer_delete_item(pass_along);
607 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
618 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
620 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
622 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
625 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
626 fi = g_malloc ( *len );
627 fi->len = strlen(sublayer) + 1;
628 memcpy(fi->data, sublayer, fi->len);
629 memcpy(fi->data + fi->len, id, il);
631 *item = (guint8 *)fi;
634 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
636 FlatItem *fi = (FlatItem *) item;
638 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
643 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
644 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
645 vik_trw_layer_add_waypoint ( vtl, name, w );
646 waypoint_convert(name, w, &vtl->coord_mode);
647 // Consider if redraw necessary for the new item
648 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
649 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
652 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
656 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
657 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
658 vik_trw_layer_add_track ( vtl, name, t );
659 track_convert(name, t, &vtl->coord_mode);
660 // Consider if redraw necessary for the new item
661 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
662 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
668 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
675 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
679 case PARAM_TV: vtl->tracks_visible = data.b; break;
680 case PARAM_WV: vtl->waypoints_visible = data.b; break;
681 case PARAM_DM: vtl->drawmode = data.u; break;
682 case PARAM_DP: vtl->drawpoints = data.b; break;
683 case PARAM_DE: vtl->drawelevation = data.b; break;
684 case PARAM_DS: vtl->drawstops = data.b; break;
685 case PARAM_DL: vtl->drawlines = data.b; break;
686 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
687 vtl->stop_length = data.u;
689 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
690 vtl->elevation_factor = data.u;
692 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
694 vtl->line_thickness = data.u;
695 trw_layer_new_track_gcs ( vtl, vp );
698 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
700 vtl->bg_line_thickness = data.u;
701 trw_layer_new_track_gcs ( vtl, vp );
706 /* Convert to store internally
707 NB file operation always in internal units (metres per second) */
708 vik_units_speed_t speed_units = a_vik_get_units_speed ();
709 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
710 vtl->velocity_min = data.d;
711 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
712 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
713 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
714 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
717 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
722 /* Convert to store internally
723 NB file operation always in internal units (metres per second) */
724 vik_units_speed_t speed_units = a_vik_get_units_speed ();
725 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
726 vtl->velocity_max = data.d;
727 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
728 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
729 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
730 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
733 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
736 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
737 case PARAM_DLA: vtl->drawlabels = data.b; break;
738 case PARAM_DI: vtl->drawimages = data.b; break;
739 case PARAM_IS: if ( data.u != vtl->image_size )
741 vtl->image_size = data.u;
742 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
743 g_queue_free ( vtl->image_cache );
744 vtl->image_cache = g_queue_new ();
747 case PARAM_IA: vtl->image_alpha = data.u; break;
748 case PARAM_ICS: vtl->image_cache_size = data.u;
749 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
750 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
752 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
753 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
754 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
755 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
756 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
757 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
758 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
763 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
765 VikLayerParamData rv;
768 case PARAM_TV: rv.b = vtl->tracks_visible; break;
769 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
770 case PARAM_DM: rv.u = vtl->drawmode; break;
771 case PARAM_DP: rv.b = vtl->drawpoints; break;
772 case PARAM_DE: rv.b = vtl->drawelevation; break;
773 case PARAM_EF: rv.u = vtl->elevation_factor; break;
774 case PARAM_DS: rv.b = vtl->drawstops; break;
775 case PARAM_SL: rv.u = vtl->stop_length; break;
776 case PARAM_DL: rv.b = vtl->drawlines; break;
777 case PARAM_LT: rv.u = vtl->line_thickness; break;
778 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
781 /* Convert to store internally
782 NB file operation always in internal units (metres per second) */
783 vik_units_speed_t speed_units = a_vik_get_units_speed ();
784 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
785 rv.d = vtl->velocity_min;
786 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
787 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
788 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
789 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
792 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
797 /* Convert to store internally
798 NB file operation always in internal units (metres per second) */
799 vik_units_speed_t speed_units = a_vik_get_units_speed ();
800 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
801 rv.d = vtl->velocity_max;
802 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
803 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
804 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
805 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
808 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
811 case PARAM_DLA: rv.b = vtl->drawlabels; break;
812 case PARAM_DI: rv.b = vtl->drawimages; break;
813 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
814 case PARAM_IS: rv.u = vtl->image_size; break;
815 case PARAM_IA: rv.u = vtl->image_alpha; break;
816 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
817 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
818 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
819 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
820 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
821 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
822 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
823 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
828 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
839 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
840 a_gpx_write_file(vtl, f);
841 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
844 g_file_get_contents(tmpname, &dd, &dl, NULL);
845 *len = sizeof(pl) + pl + dl;
846 *data = g_malloc(*len);
847 memcpy(*data, &pl, sizeof(pl));
848 memcpy(*data + sizeof(pl), pd, pl);
849 memcpy(*data + sizeof(pl) + pl, dd, dl);
858 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
860 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
866 memcpy(&pl, data, sizeof(pl));
868 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
871 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
872 g_critical("couldn't open temp file");
875 fwrite(data, len - pl - sizeof(pl), 1, f);
877 a_gpx_read_file(rv, f);
885 static GList * str_array_to_glist(gchar* data[])
889 for (p = (gpointer)data; *p; p++)
890 gl = g_list_prepend(gl, *p);
891 return(g_list_reverse(gl));
894 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
896 return (strcasecmp(s1, s2) == 0);
899 static guint strcase_hash(gconstpointer v)
901 /* 31 bit hash function */
904 gchar s[128]; /* malloc is too slow for reading big files */
907 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
908 p[i] = toupper(t[i]);
914 for (p += 1; *p != '\0'; p++)
915 h = (h << 5) - h + *p;
921 static VikTrwLayer* trw_layer_new ( gint drawmode )
923 if (trw_layer_params[PARAM_DM].widget_data == NULL)
924 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
925 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
926 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
928 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
929 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
931 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
932 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
933 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
934 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
936 /* TODO: constants at top */
937 rv->waypoints_visible = rv->tracks_visible = TRUE;
938 rv->drawmode = drawmode;
939 rv->drawpoints = TRUE;
940 rv->drawstops = FALSE;
941 rv->drawelevation = FALSE;
942 rv->elevation_factor = 30;
943 rv->stop_length = 60;
944 rv->drawlines = TRUE;
945 rv->wplabellayout = NULL;
946 rv->wp_right_click_menu = NULL;
947 rv->track_right_click_menu = NULL;
948 rv->waypoint_gc = NULL;
949 rv->waypoint_text_gc = NULL;
950 rv->waypoint_bg_gc = NULL;
952 rv->velocity_max = 5.0;
953 rv->velocity_min = 0.0;
954 rv->line_thickness = 1;
955 rv->bg_line_thickness = 0;
956 rv->current_wp = NULL;
957 rv->current_wp_name = NULL;
958 rv->current_track = NULL;
959 rv->current_tpl = NULL;
960 rv->current_tp_track_name = NULL;
961 rv->moving_tp = FALSE;
962 rv->moving_wp = FALSE;
964 rv->ct_sync_done = TRUE;
966 rv->route_finder_started = FALSE;
967 rv->route_finder_check_added_track = FALSE;
968 rv->route_finder_added_track_name = NULL;
969 rv->route_finder_current_track = NULL;
970 rv->route_finder_append = FALSE;
972 rv->waypoint_rightclick = FALSE;
974 rv->last_tp_track_name = NULL;
976 rv->image_cache = g_queue_new();
978 rv->image_alpha = 255;
979 rv->image_cache_size = 300;
980 rv->drawimages = TRUE;
981 rv->drawlabels = TRUE;
986 static void trw_layer_free ( VikTrwLayer *trwlayer )
988 g_hash_table_destroy(trwlayer->waypoints);
989 g_hash_table_destroy(trwlayer->tracks);
991 /* ODC: replace with GArray */
992 trw_layer_free_track_gcs ( trwlayer );
994 if ( trwlayer->wp_right_click_menu )
995 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
997 if ( trwlayer->track_right_click_menu )
998 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1000 if ( trwlayer->wplabellayout != NULL)
1001 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1003 if ( trwlayer->waypoint_gc != NULL )
1004 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1006 if ( trwlayer->waypoint_text_gc != NULL )
1007 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1009 if ( trwlayer->waypoint_bg_gc != NULL )
1010 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1012 if ( trwlayer->waypoint_font != NULL )
1013 gdk_font_unref ( trwlayer->waypoint_font );
1015 if ( trwlayer->tpwin != NULL )
1016 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1018 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1019 g_queue_free ( trwlayer->image_cache );
1022 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
1025 dp->xmpp = vik_viewport_get_xmpp ( vp );
1026 dp->ympp = vik_viewport_get_ympp ( vp );
1027 dp->width = vik_viewport_get_width ( vp );
1028 dp->height = vik_viewport_get_height ( vp );
1029 dp->center = vik_viewport_get_center ( vp );
1030 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1031 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1036 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1037 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1038 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1040 dp->ce1 = dp->center->east_west-w2;
1041 dp->ce2 = dp->center->east_west+w2;
1042 dp->cn1 = dp->center->north_south-h2;
1043 dp->cn2 = dp->center->north_south+h2;
1044 } else if ( dp->lat_lon ) {
1045 VikCoord upperleft, bottomright;
1046 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1047 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1048 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1049 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1050 dp->ce1 = upperleft.east_west;
1051 dp->ce2 = bottomright.east_west;
1052 dp->cn1 = bottomright.north_south;
1053 dp->cn2 = upperleft.north_south;
1056 dp->track_gc_iter = 0;
1059 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1061 static gdouble rv = 0;
1062 if ( tp1->has_timestamp && tp2->has_timestamp )
1064 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1065 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1068 return VIK_TRW_LAYER_TRACK_GC_MIN;
1069 else if ( vtl->velocity_min >= vtl->velocity_max )
1070 return VIK_TRW_LAYER_TRACK_GC_MAX;
1072 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1074 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1075 return VIK_TRW_LAYER_TRACK_GC_MAX;
1079 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1082 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1084 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1085 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1086 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1087 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1090 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1092 /* TODO: this function is a mess, get rid of any redundancy */
1093 GList *list = track->trackpoints;
1095 gboolean useoldvals = TRUE;
1097 gboolean drawpoints;
1099 gboolean drawelevation;
1100 gdouble min_alt, max_alt, alt_diff = 0;
1102 const guint8 tp_size_reg = 2;
1103 const guint8 tp_size_cur = 4;
1106 if ( dp->vtl->drawelevation )
1108 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1109 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1110 alt_diff = max_alt - min_alt;
1113 if ( ! track->visible )
1116 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1117 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1118 trw_layer_draw_track ( name, track, dp, TRUE );
1120 if ( drawing_white_background )
1121 drawpoints = drawstops = FALSE;
1123 drawpoints = dp->vtl->drawpoints;
1124 drawstops = dp->vtl->drawstops;
1127 /* Current track - used for creation */
1128 if ( track == dp->vtl->current_track )
1129 main_gc = dp->vtl->current_track_gc;
1131 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1132 /* Draw all tracks of the layer in special colour */
1133 /* if track is member of selected layer or is the current selected track
1134 then draw in the highlight colour.
1135 NB this supercedes the drawmode */
1136 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1137 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1138 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1139 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1142 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1143 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1145 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1149 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1150 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1152 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1157 int x, y, oldx, oldy;
1158 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1160 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1162 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1164 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1166 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1167 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1173 while ((list = g_list_next(list)))
1175 tp = VIK_TRACKPOINT(list->data);
1176 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1178 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1179 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1180 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1181 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1182 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1184 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1187 * If points are the same in display coordinates, don't draw.
1189 if ( useoldvals && x == oldx && y == oldy )
1191 // Still need to process points to ensure 'stops' are drawn if required
1192 if ( drawstops && drawpoints && ! drawing_white_background && list->next &&
1193 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1194 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 );
1199 if ( drawpoints && ! drawing_white_background )
1203 * The concept of drawing stops is that a trackpoint
1204 * that is if the next trackpoint has a timestamp far into
1205 * the future, we draw a circle of 6x trackpoint size,
1206 * instead of a rectangle of 2x trackpoint size.
1207 * This is drawn first so the trackpoint will be drawn on top
1210 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1211 /* Stop point. Draw 6x circle. */
1212 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 );
1214 /* Regular point - draw 2x square. */
1215 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1218 /* Final point - draw 4x circle. */
1219 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 );
1222 if ((!tp->newsegment) && (dp->vtl->drawlines))
1224 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1226 /* UTM only: zone check */
1227 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1228 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1230 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1231 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1232 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1236 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1238 if ( drawing_white_background ) {
1239 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1243 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1244 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1246 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1251 tmp[1].y = oldy-FIXALTITUDE(list->data);
1253 tmp[2].y = y-FIXALTITUDE(list->next->data);
1258 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1259 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1261 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1262 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1264 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1275 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1277 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1278 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1280 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1281 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1282 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1283 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1287 * If points are the same in display coordinates, don't draw.
1289 if ( x != oldx || y != oldy )
1291 if ( drawing_white_background )
1292 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1294 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1300 * If points are the same in display coordinates, don't draw.
1302 if ( x != oldx && y != oldy )
1304 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1305 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1313 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1314 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1315 dp->track_gc_iter = 0;
1318 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1319 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1321 trw_layer_draw_track ( name, track, dp, FALSE );
1324 static void cached_pixbuf_free ( CachedPixbuf *cp )
1326 g_object_unref ( G_OBJECT(cp->pixbuf) );
1327 g_free ( cp->image );
1330 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1332 return strcmp ( cp->image, name );
1335 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1338 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1339 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1340 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1343 GdkPixbuf *sym = NULL;
1344 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1346 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1348 if ( wp->image && dp->vtl->drawimages )
1350 GdkPixbuf *pixbuf = NULL;
1353 if ( dp->vtl->image_alpha == 0)
1356 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1358 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1361 gchar *image = wp->image;
1362 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1363 if ( ! regularthumb )
1365 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1366 image = "\x12\x00"; /* this shouldn't occur naturally. */
1370 CachedPixbuf *cp = NULL;
1371 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1372 if ( dp->vtl->image_size == 128 )
1373 cp->pixbuf = regularthumb;
1376 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1377 g_assert ( cp->pixbuf );
1378 g_object_unref ( G_OBJECT(regularthumb) );
1380 cp->image = g_strdup ( image );
1382 /* needed so 'click picture' tool knows how big the pic is; we don't
1383 * store it in cp because they may have been freed already. */
1384 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1385 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1387 g_queue_push_head ( dp->vtl->image_cache, cp );
1388 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1389 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1391 pixbuf = cp->pixbuf;
1395 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1401 w = gdk_pixbuf_get_width ( pixbuf );
1402 h = gdk_pixbuf_get_height ( pixbuf );
1404 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1406 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1407 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1408 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1409 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1410 // Highlighted - so draw a little border around the chosen one
1411 // single line seems a little weak so draw 2 of them
1412 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1413 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1414 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1415 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1418 if ( dp->vtl->image_alpha == 255 )
1419 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1421 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1423 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1427 /* DRAW ACTUAL DOT */
1428 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1429 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 );
1431 else if ( wp == dp->vtl->current_wp ) {
1432 switch ( dp->vtl->wp_symbol ) {
1433 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;
1434 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;
1435 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;
1436 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 );
1437 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 );
1441 switch ( dp->vtl->wp_symbol ) {
1442 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;
1443 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;
1444 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;
1445 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 );
1446 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;
1450 if ( dp->vtl->drawlabels )
1452 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1453 gint label_x, label_y;
1455 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1456 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1457 label_x = x - width/2;
1459 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1461 label_y = y - dp->vtl->wp_size - height - 2;
1463 /* if highlight mode on, then draw background text in highlight colour */
1464 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1465 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1466 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1467 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1468 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1470 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1473 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1475 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1480 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1482 static struct DrawingParams dp;
1483 g_assert ( l != NULL );
1485 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1488 if ( l->tracks_visible )
1489 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1491 if (l->waypoints_visible)
1492 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1495 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1498 if ( vtl->track_bg_gc )
1500 g_object_unref ( vtl->track_bg_gc );
1501 vtl->track_bg_gc = NULL;
1503 if ( vtl->current_track_gc )
1505 g_object_unref ( vtl->current_track_gc );
1506 vtl->current_track_gc = NULL;
1509 if ( ! vtl->track_gc )
1511 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1512 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1513 g_array_free ( vtl->track_gc, TRUE );
1514 vtl->track_gc = NULL;
1517 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1519 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1520 gint width = vtl->line_thickness;
1522 if ( vtl->track_gc )
1523 trw_layer_free_track_gcs ( vtl );
1525 if ( vtl->track_bg_gc )
1526 g_object_unref ( vtl->track_bg_gc );
1527 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1529 if ( vtl->current_track_gc )
1530 g_object_unref ( vtl->current_track_gc );
1531 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1532 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1534 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1536 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1538 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1539 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1540 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1541 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1542 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1543 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1544 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1545 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1546 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1547 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1549 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1551 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1553 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1556 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1558 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1559 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1561 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1562 /* early exit, as the rest is GUI related */
1566 PangoFontDescription *pfd;
1567 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1568 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1569 pango_layout_set_font_description (rv->wplabellayout, pfd);
1570 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1571 pango_font_description_free (pfd);
1573 trw_layer_new_track_gcs ( rv, vp );
1575 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1576 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1577 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1578 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1580 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1582 rv->has_verified_thumbnails = FALSE;
1583 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1585 rv->wp_draw_symbols = TRUE;
1587 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1589 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1594 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1596 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1598 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1599 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 );
1601 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 );
1604 *new_iter = *((GtkTreeIter *) pass_along[1]);
1605 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1607 if ( ! track->visible )
1608 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1611 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1613 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1614 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1615 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 );
1617 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 );
1620 *new_iter = *((GtkTreeIter *) pass_along[1]);
1621 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1623 if ( ! wp->visible )
1624 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1628 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1631 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1633 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1634 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1636 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1638 if ( ! vtl->tracks_visible )
1639 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1641 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1643 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1644 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1646 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1649 if ( ! vtl->waypoints_visible )
1650 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1652 pass_along[0] = &(vtl->waypoints_iter);
1653 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1655 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1659 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1663 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1664 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1665 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1667 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1669 return (t->visible ^= 1);
1673 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1675 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1677 return (t->visible ^= 1);
1686 * Return a property about tracks for this layer
1688 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1690 return vtl->line_thickness;
1693 // Structure to hold multiple track information for a layer
1702 * Build up layer multiple track information via updating the tooltip_tracks structure
1704 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1706 tt->length = tt->length + vik_track_get_length (tr);
1708 // Ensure times are available
1709 if ( tr->trackpoints &&
1710 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1711 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1714 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1715 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1717 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1718 // Hence initialize to the first 'proper' value
1719 if ( tt->start_time == 0 )
1720 tt->start_time = t1;
1721 if ( tt->end_time == 0 )
1724 // Update find the earliest / last times
1725 if ( t1 < tt->start_time )
1726 tt->start_time = t1;
1727 if ( t2 > tt->end_time )
1730 // Keep track of total time
1731 // there maybe gaps within a track (eg segments)
1732 // but this should be generally good enough for a simple indicator
1733 tt->duration = tt->duration + (int)(t2-t1);
1738 * Generate tooltip text for the layer.
1739 * This is relatively complicated as it considers information for
1740 * no tracks, a single track or multiple tracks
1741 * (which may or may not have timing information)
1743 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1754 static gchar tmp_buf[128];
1757 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1759 // Safety check - I think these should always be valid
1760 if ( vtl->tracks && vtl->waypoints ) {
1761 tooltip_tracks tt = { 0.0, 0, 0 };
1762 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1764 GDate* gdate_start = g_date_new ();
1765 g_date_set_time_t (gdate_start, tt.start_time);
1767 GDate* gdate_end = g_date_new ();
1768 g_date_set_time_t (gdate_end, tt.end_time);
1770 if ( g_date_compare (gdate_start, gdate_end) ) {
1771 // Dates differ so print range on separate line
1772 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1773 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1774 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1777 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1778 if ( tt.start_time != 0 )
1779 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1783 if ( tt.length > 0.0 ) {
1784 gdouble len_in_units;
1786 // Setup info dependent on distance units
1787 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1788 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1789 len_in_units = VIK_METERS_TO_MILES(tt.length);
1792 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1793 len_in_units = tt.length/1000.0;
1796 // Timing information if available
1798 if ( tt.duration > 0 ) {
1799 g_snprintf (tbuf1, sizeof(tbuf1),
1800 _(" in %d:%02d hrs:mins"),
1801 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1803 g_snprintf (tbuf2, sizeof(tbuf2),
1804 _("\n%sTotal Length %.1f %s%s"),
1805 tbuf3, len_in_units, tbuf4, tbuf1);
1808 // Put together all the elements to form compact tooltip text
1809 g_snprintf (tmp_buf, sizeof(tmp_buf),
1810 _("Tracks: %d - Waypoints: %d%s"),
1811 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1813 g_date_free (gdate_start);
1814 g_date_free (gdate_end);
1821 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1825 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1826 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1827 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1829 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1831 // Could be a better way of handling strings - but this works...
1832 gchar time_buf1[20];
1833 gchar time_buf2[20];
1834 time_buf1[0] = '\0';
1835 time_buf2[0] = '\0';
1836 static gchar tmp_buf[100];
1837 // Compact info: Short date eg (11/20/99), duration and length
1838 // Hopefully these are the things that are most useful and so promoted into the tooltip
1839 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1840 // %x The preferred date representation for the current locale without the time.
1841 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1842 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1843 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1845 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1848 // Get length and consider the appropriate distance units
1849 gdouble tr_len = vik_track_get_length(tr);
1850 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1851 switch (dist_units) {
1852 case VIK_UNITS_DISTANCE_KILOMETRES:
1853 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1855 case VIK_UNITS_DISTANCE_MILES:
1856 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1865 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1867 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1868 // NB It's OK to return NULL
1878 * Function to show basic track point information on the statusbar
1880 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1883 switch (a_vik_get_units_height ()) {
1884 case VIK_UNITS_HEIGHT_FEET:
1885 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1888 //VIK_UNITS_HEIGHT_METRES:
1889 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1894 if ( trkpt->has_timestamp ) {
1895 // Compact date time format
1896 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1900 // Position is put later on, as this bit may not be seen if the display is not big enough,
1901 // one can easily use the current pointer position to see this if needed
1902 gchar *lat = NULL, *lon = NULL;
1903 static struct LatLon ll;
1904 vik_coord_to_latlon (&(trkpt->coord), &ll);
1905 a_coords_latlon_to_string ( &ll, &lat, &lon );
1908 // Again is put later on, as this bit may not be seen if the display is not big enough
1909 // trackname can be seen from the treeview (when enabled)
1910 // Also name could be very long to not leave room for anything else
1913 if ( vtl->current_tp_track_name ) {
1914 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track_name );
1917 // Combine parts to make overall message
1918 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
1919 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1926 * Function to show basic waypoint information on the statusbar
1928 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
1931 switch (a_vik_get_units_height ()) {
1932 case VIK_UNITS_HEIGHT_FEET:
1933 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
1936 //VIK_UNITS_HEIGHT_METRES:
1937 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
1941 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
1942 // one can easily use the current pointer position to see this if needed
1943 gchar *lat = NULL, *lon = NULL;
1944 static struct LatLon ll;
1945 vik_coord_to_latlon (&(wpt->coord), &ll);
1946 a_coords_latlon_to_string ( &ll, &lat, &lon );
1948 // Combine parts to make overall message
1951 // Add comment if available
1952 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
1954 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
1955 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1962 * General layer selection function, find out which bit is selected and take appropriate action
1964 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1967 l->current_wp = NULL;
1968 l->current_wp_name = NULL;
1969 trw_layer_cancel_current_tp ( l, FALSE );
1972 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
1976 case VIK_TREEVIEW_TYPE_LAYER:
1978 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1979 /* Mark for redraw */
1984 case VIK_TREEVIEW_TYPE_SUBLAYER:
1988 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1990 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1991 /* Mark for redraw */
1995 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1997 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
1998 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
1999 /* Mark for redraw */
2003 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2005 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2006 /* Mark for redraw */
2010 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2012 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2013 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
2014 // Show some waypoint info
2015 set_statusbar_msg_info_wpt ( l, wpt );
2016 /* Mark for redraw */
2022 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2031 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2036 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2041 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2043 return l->waypoints;
2046 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2048 static VikCoord fixme;
2049 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2050 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2051 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2052 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2053 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2054 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2055 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2056 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2057 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2060 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
2063 static VikCoord fixme;
2067 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2068 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2069 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2070 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2071 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2072 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2073 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2074 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2075 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2080 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2082 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2083 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2085 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2086 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2087 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2088 maxmin[0].lat = wpt_maxmin[0].lat;
2091 maxmin[0].lat = trk_maxmin[0].lat;
2093 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2094 maxmin[0].lon = wpt_maxmin[0].lon;
2097 maxmin[0].lon = trk_maxmin[0].lon;
2099 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2100 maxmin[1].lat = wpt_maxmin[1].lat;
2103 maxmin[1].lat = trk_maxmin[1].lat;
2105 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2106 maxmin[1].lon = wpt_maxmin[1].lon;
2109 maxmin[1].lon = trk_maxmin[1].lon;
2113 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2115 /* 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... */
2116 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2117 trw_layer_find_maxmin (vtl, maxmin);
2118 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2122 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2123 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2128 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2131 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2132 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2134 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2137 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2139 /* First set the center [in case previously viewing from elsewhere] */
2140 /* Then loop through zoom levels until provided positions are in view */
2141 /* This method is not particularly fast - but should work well enough */
2142 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2144 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2145 vik_viewport_set_center_coord ( vvp, &coord );
2147 /* Convert into definite 'smallest' and 'largest' positions */
2148 struct LatLon minmin;
2149 if ( maxmin[0].lat < maxmin[1].lat )
2150 minmin.lat = maxmin[0].lat;
2152 minmin.lat = maxmin[1].lat;
2154 struct LatLon maxmax;
2155 if ( maxmin[0].lon > maxmin[1].lon )
2156 maxmax.lon = maxmin[0].lon;
2158 maxmax.lon = maxmin[1].lon;
2160 /* Never zoom in too far - generally not that useful, as too close ! */
2161 /* Always recalculate the 'best' zoom level */
2163 vik_viewport_set_zoom ( vvp, zoom );
2165 gdouble min_lat, max_lat, min_lon, max_lon;
2166 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2167 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2168 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2169 /* NB I think the logic used in this test to determine if the bounds is within view
2170 fails if track goes across 180 degrees longitude.
2171 Hopefully that situation is not too common...
2172 Mind you viking doesn't really do edge locations to well anyway */
2173 if ( min_lat < minmin.lat &&
2174 max_lat > minmin.lat &&
2175 min_lon < maxmax.lon &&
2176 max_lon > maxmax.lon )
2177 /* Found within zoom level */
2182 vik_viewport_set_zoom ( vvp, zoom );
2186 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2188 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2189 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2190 trw_layer_find_maxmin (vtl, maxmin);
2191 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2194 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2199 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2201 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])) ) ) {
2202 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2205 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2208 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2210 GtkWidget *file_selector;
2212 gboolean failed = FALSE;
2213 file_selector = gtk_file_chooser_dialog_new (title,
2215 GTK_FILE_CHOOSER_ACTION_SAVE,
2216 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2217 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2219 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2221 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2223 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2224 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2226 gtk_widget_hide ( file_selector );
2227 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2232 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2234 gtk_widget_hide ( file_selector );
2235 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2240 gtk_widget_destroy ( file_selector );
2242 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2245 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2247 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2250 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2252 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2255 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2257 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2258 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2259 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2260 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2262 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2264 g_free ( auto_save_name );
2267 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2269 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2270 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2271 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2272 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2274 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2276 g_free ( auto_save_name );
2280 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2283 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2285 gchar *name_used = NULL;
2288 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2289 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL);
2291 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2295 gchar *quoted_file = g_shell_quote ( name_used );
2296 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2297 g_free ( quoted_file );
2298 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2300 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2301 g_error_free ( err );
2305 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2306 //g_remove ( name_used );
2307 // Perhaps should be deleted when the program ends?
2308 // For now leave it to the user to delete it / use system temp cleanup methods.
2309 g_free ( name_used );
2313 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2315 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2318 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2320 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2323 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2325 gpointer layer_and_vlp[2];
2326 layer_and_vlp[0] = pass_along[0];
2327 layer_and_vlp[1] = pass_along[1];
2329 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2330 gchar *auto_save_name = g_strdup ( pass_along[3] );
2331 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2332 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2334 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2336 g_free ( auto_save_name );
2339 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2341 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2342 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2343 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2344 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2346 GTK_RESPONSE_REJECT,
2348 GTK_RESPONSE_ACCEPT,
2351 GtkWidget *label, *entry;
2352 label = gtk_label_new(_("Waypoint Name:"));
2353 entry = gtk_entry_new();
2355 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2356 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2357 gtk_widget_show_all ( label );
2358 gtk_widget_show_all ( entry );
2360 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2362 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2365 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2368 for ( i = strlen(upname)-1; i >= 0; i-- )
2369 upname[i] = toupper(upname[i]);
2371 wp = g_hash_table_lookup ( wps, upname );
2374 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2377 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2378 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2379 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 );
2386 gtk_widget_destroy ( dia );
2389 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2391 gchar *default_name = highest_wp_number_get(vtl);
2392 VikWaypoint *wp = vik_waypoint_new();
2393 gchar *returned_name;
2395 wp->coord = *def_coord;
2397 // Attempt to auto set height if DEM data is available
2398 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2399 if ( elev != VIK_DEM_INVALID_ELEVATION )
2400 wp->altitude = (gdouble)elev;
2402 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2405 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2406 g_free (default_name);
2409 g_free (default_name);
2410 vik_waypoint_free(wp);
2414 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2417 struct LatLon one_ll, two_ll;
2418 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2420 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2421 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2422 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2423 VikViewport *vvp = vik_window_viewport(vw);
2424 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2425 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2426 vik_coord_to_latlon(&one, &one_ll);
2427 vik_coord_to_latlon(&two, &two_ll);
2428 if (one_ll.lat > two_ll.lat) {
2429 maxmin[0].lat = one_ll.lat;
2430 maxmin[1].lat = two_ll.lat;
2433 maxmin[0].lat = two_ll.lat;
2434 maxmin[1].lat = one_ll.lat;
2436 if (one_ll.lon > two_ll.lon) {
2437 maxmin[0].lon = one_ll.lon;
2438 maxmin[1].lon = two_ll.lon;
2441 maxmin[0].lon = two_ll.lon;
2442 maxmin[1].lon = one_ll.lon;
2444 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2447 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2449 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2450 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2451 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2453 trw_layer_find_maxmin (vtl, maxmin);
2454 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2457 #ifdef VIK_CONFIG_GEOTAG
2458 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2460 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2462 // Update directly - not changing the mtime
2463 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2466 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2468 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2471 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2475 * Use code in separate file for this feature as reasonably complex
2477 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2479 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2480 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2481 // Unset so can be reverified later if necessary
2482 vtl->has_verified_thumbnails = FALSE;
2484 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2490 static void trw_layer_geotagging ( gpointer lav[2] )
2492 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2493 // Unset so can be reverified later if necessary
2494 vtl->has_verified_thumbnails = FALSE;
2496 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2503 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2506 * Acquire into this TRW Layer straight from GPS Device
2508 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2510 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2511 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2512 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2513 VikViewport *vvp = vik_window_viewport(vw);
2515 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2516 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2520 * Acquire into this TRW Layer from Google Directions
2522 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2524 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2525 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2526 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2527 VikViewport *vvp = vik_window_viewport(vw);
2529 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2532 #ifdef VIK_CONFIG_OPENSTREETMAP
2534 * Acquire into this TRW Layer from OSM
2536 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2538 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2539 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2540 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2541 VikViewport *vvp = vik_window_viewport(vw);
2543 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2547 #ifdef VIK_CONFIG_GEOCACHES
2549 * Acquire into this TRW Layer from Geocaching.com
2551 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2553 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2554 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2555 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2556 VikViewport *vvp = vik_window_viewport(vw);
2558 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2562 #ifdef VIK_CONFIG_GEOTAG
2564 * Acquire into this TRW Layer from images
2566 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2568 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2569 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2570 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2571 VikViewport *vvp = vik_window_viewport(vw);
2573 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2574 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2576 // Reverify thumbnails as they may have changed
2577 vtl->has_verified_thumbnails = FALSE;
2578 trw_layer_verify_thumbnails ( vtl, NULL );
2582 static void trw_layer_new_wp ( gpointer lav[2] )
2584 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2585 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2586 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2587 instead return true if you want to update. */
2588 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 )
2589 vik_layers_panel_emit_update ( vlp );
2592 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2594 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2595 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2597 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2598 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2599 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2600 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2601 vik_layers_panel_emit_update ( vlp );
2605 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2607 /* NB do not care if wp is visible or not */
2608 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2611 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2613 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2614 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2616 /* Only 1 waypoint - jump straight to it */
2617 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2618 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2619 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2621 /* If at least 2 waypoints - find center and then zoom to fit */
2622 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2624 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2625 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2626 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2629 vik_layers_panel_emit_update ( vlp );
2632 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2634 static gpointer pass_along[2];
2636 GtkWidget *export_submenu;
2637 pass_along[0] = vtl;
2638 pass_along[1] = vlp;
2640 item = gtk_menu_item_new();
2641 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2642 gtk_widget_show ( item );
2644 /* Now with icons */
2645 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2646 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2647 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2648 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2649 gtk_widget_show ( item );
2651 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2652 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2653 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2654 gtk_widget_show ( item );
2656 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2657 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2658 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2659 gtk_widget_show ( item );
2661 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2662 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2663 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2664 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2665 gtk_widget_show ( item );
2667 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2668 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2669 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2670 gtk_widget_show ( item );
2672 export_submenu = gtk_menu_new ();
2673 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2674 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2675 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2676 gtk_widget_show ( item );
2677 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2679 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2680 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2681 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2682 gtk_widget_show ( item );
2684 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2685 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2686 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2687 gtk_widget_show ( item );
2689 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2690 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2691 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2692 gtk_widget_show ( item );
2694 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2695 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2696 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2697 gtk_widget_show ( item );
2699 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
2700 item = gtk_menu_item_new_with_mnemonic ( external1 );
2701 g_free ( external1 );
2702 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
2703 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2704 gtk_widget_show ( item );
2706 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
2707 item = gtk_menu_item_new_with_mnemonic ( external2 );
2708 g_free ( external2 );
2709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
2710 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2711 gtk_widget_show ( item );
2713 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2716 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2717 gtk_widget_show ( item );
2719 #ifdef VIK_CONFIG_GEONAMES
2720 GtkWidget *wikipedia_submenu = gtk_menu_new();
2721 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2722 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2723 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2724 gtk_widget_show(item);
2725 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2727 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2728 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2729 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2730 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2731 gtk_widget_show ( item );
2733 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2734 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2735 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2736 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2737 gtk_widget_show ( item );
2740 #ifdef VIK_CONFIG_GEOTAG
2741 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
2742 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
2743 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2744 gtk_widget_show ( item );
2747 GtkWidget *acquire_submenu = gtk_menu_new ();
2748 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2749 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2750 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2751 gtk_widget_show ( item );
2752 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2754 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2755 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2756 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2757 gtk_widget_show ( item );
2759 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2760 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2761 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2762 gtk_widget_show ( item );
2764 #ifdef VIK_CONFIG_OPENSTREETMAP
2765 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2766 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2767 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2768 gtk_widget_show ( item );
2771 #ifdef VIK_CONFIG_GEOCACHES
2772 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2773 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2774 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2775 gtk_widget_show ( item );
2778 #ifdef VIK_CONFIG_GEOTAG
2779 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
2780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
2781 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2782 gtk_widget_show ( item );
2785 #ifdef VIK_CONFIG_OPENSTREETMAP
2786 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2787 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2788 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2789 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2790 gtk_widget_show ( item );
2793 GtkWidget *delete_submenu = gtk_menu_new ();
2794 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2795 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2796 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2797 gtk_widget_show ( item );
2798 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2800 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2801 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2803 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2804 gtk_widget_show ( item );
2806 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2807 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2809 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2810 gtk_widget_show ( item );
2812 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2815 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2816 gtk_widget_show ( item );
2818 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2819 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2820 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2821 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2822 gtk_widget_show ( item );
2824 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2825 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2827 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2828 gtk_widget_show ( item );
2831 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2832 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2834 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2835 gtk_widget_show ( item );
2839 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2841 if ( VIK_LAYER(vtl)->realized )
2843 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2845 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2848 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2849 // Visibility column always needed for waypoints
2850 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2851 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2853 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2855 // Actual setting of visibility dependent on the waypoint
2856 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2857 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2861 highest_wp_number_add_wp(vtl, name);
2862 g_hash_table_insert ( vtl->waypoints, name, wp );
2866 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2868 if ( VIK_LAYER(vtl)->realized )
2870 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2872 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2875 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2876 // Visibility column always needed for tracks
2877 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2878 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2880 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2882 // Actual setting of visibility dependent on the track
2883 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2884 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2888 g_hash_table_insert ( vtl->tracks, name, t );
2892 /* to be called whenever a track has been deleted or may have been changed. */
2893 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2895 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2896 trw_layer_cancel_current_tp ( vtl, FALSE );
2897 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2898 trw_layer_cancel_last_tp ( vtl );
2901 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2904 gchar *newname = g_strdup(name);
2905 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2906 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2907 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2909 newname = new_newname;
2915 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2917 vik_trw_layer_add_waypoint ( vtl,
2918 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2921 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2923 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
2924 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2925 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
2926 vik_track_free ( tr );
2927 vtl->route_finder_append = FALSE; /* this means we have added it */
2929 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2930 vik_trw_layer_add_track ( vtl, new_name, tr );
2932 if ( vtl->route_finder_check_added_track ) {
2933 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2934 if ( vtl->route_finder_added_track_name ) /* for google routes */
2935 g_free ( vtl->route_finder_added_track_name );
2936 vtl->route_finder_added_track_name = g_strdup(new_name);
2941 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2943 *l = g_list_append(*l, (gpointer)name);
2946 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2948 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2949 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2951 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2952 vik_trw_layer_delete_track(vtl_src, name);
2953 vik_trw_layer_add_track(vtl_dest, newname, t);
2955 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2957 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2958 vik_trw_layer_delete_waypoint(vtl_src, name);
2959 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2963 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2965 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2966 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2968 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2969 GList *items = NULL;
2972 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2973 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2975 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2976 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2981 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2982 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2984 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2991 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2992 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2996 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2998 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2999 gboolean was_visible = FALSE;
3003 was_visible = t->visible;
3004 if ( t == vtl->current_track ) {
3005 vtl->current_track = NULL;
3007 if ( t == vtl->route_finder_current_track )
3008 vtl->route_finder_current_track = NULL;
3010 /* could be current_tp, so we have to check */
3011 trw_layer_cancel_tps_of_track ( vtl, trk_name );
3013 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
3014 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3015 g_hash_table_remove ( vtl->tracks_iters, trk_name );
3017 /* do this last because trk_name may be pointing to actual orig key */
3018 g_hash_table_remove ( vtl->tracks, trk_name );
3023 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
3025 gboolean was_visible = FALSE;
3028 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
3032 if ( wp == vtl->current_wp ) {
3033 vtl->current_wp = NULL;
3034 vtl->current_wp_name = NULL;
3035 vtl->moving_wp = FALSE;
3038 was_visible = wp->visible;
3039 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
3040 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3041 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
3043 highest_wp_number_remove_wp(vtl, wp_name);
3044 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
3050 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3052 vik_treeview_item_delete (vt, it );
3055 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3058 vtl->current_track = NULL;
3059 vtl->route_finder_current_track = NULL;
3060 if (vtl->current_tp_track_name)
3061 trw_layer_cancel_current_tp(vtl, FALSE);
3062 if (vtl->last_tp_track_name)
3063 trw_layer_cancel_last_tp ( vtl );
3065 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3066 g_hash_table_remove_all(vtl->tracks_iters);
3067 g_hash_table_remove_all(vtl->tracks);
3069 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3072 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3074 vtl->current_wp = NULL;
3075 vtl->current_wp_name = NULL;
3076 vtl->moving_wp = FALSE;
3078 highest_wp_number_reset(vtl);
3080 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3081 g_hash_table_remove_all(vtl->waypoints_iters);
3082 g_hash_table_remove_all(vtl->waypoints);
3084 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3087 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3089 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3090 // Get confirmation from the user
3091 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3092 _("Are you sure you want to delete all tracks in %s?"),
3093 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3094 vik_trw_layer_delete_all_tracks (vtl);
3097 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3099 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3100 // Get confirmation from the user
3101 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3102 _("Are you sure you want to delete all waypoints in %s?"),
3103 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3104 vik_trw_layer_delete_all_waypoints (vtl);
3107 static void trw_layer_delete_item ( gpointer pass_along[6] )
3109 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3110 gboolean was_visible = FALSE;
3111 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3113 if ( GPOINTER_TO_INT ( pass_along[4]) )
3114 // Get confirmation from the user
3115 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3116 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3117 _("Are you sure you want to delete the waypoint \"%s\""),
3120 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
3124 if ( GPOINTER_TO_INT ( pass_along[4]) )
3125 // Get confirmation from the user
3126 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3127 _("Are you sure you want to delete the track \"%s\""),
3130 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
3133 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3137 static void trw_layer_properties_item ( gpointer pass_along[6] )
3139 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3140 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3142 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3145 gboolean updated = FALSE;
3146 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
3148 if ( updated && VIK_LAYER(vtl)->visible )
3149 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3154 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3157 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3159 pass_along[1], /* vlp */
3160 pass_along[3], /* track name */
3161 pass_along[5] ); /* vvp */
3167 Parameter 1 -> VikLayersPanel
3168 Parameter 2 -> VikLayer
3169 Parameter 3 -> VikViewport
3171 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3174 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3175 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3178 /* since vlp not set, vl & vvp should be valid instead! */
3180 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3181 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3186 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3188 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3189 if ( trps && trps->data )
3190 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3193 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3195 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
3196 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3197 if ( trps && *trps )
3199 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3201 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3202 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3203 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3204 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3205 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3209 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3211 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3212 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3214 vtl->current_track = track;
3215 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3217 if ( track->trackpoints )
3218 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3222 * extend a track using route finder
3224 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3226 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3227 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3228 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3230 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3231 vtl->route_finder_coord = last_coord;
3232 vtl->route_finder_current_track = track;
3233 vtl->route_finder_started = TRUE;
3235 if ( track->trackpoints )
3236 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3240 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3242 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3243 /* Also warn if overwrite old elevation data */
3244 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3246 vik_track_apply_dem_data ( track );
3249 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3251 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3254 trps = g_list_last(trps);
3255 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3258 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3260 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3263 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3266 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3268 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3271 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3274 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3276 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3279 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3283 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3285 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3287 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3288 if ( trps && *trps )
3290 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3291 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3292 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3293 if ( pass_along[1] )
3294 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3296 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3300 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3302 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3303 trw_layer_tpwin_init ( vtl );
3306 /*************************************
3307 * merge/split by time routines
3308 *************************************/
3310 /* called for each key in track hash table.
3311 * If the current track has the same time stamp type, add it to the result,
3312 * except the one pointed by "exclude".
3313 * set exclude to NULL if there is no exclude to check.
3314 * Note that the result is in reverse (for performance reasons).
3319 gboolean with_timestamps;
3321 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3323 twt_udata *user_data = udata;
3324 VikTrackpoint *p1, *p2;
3326 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3330 if (VIK_TRACK(value)->trackpoints) {
3331 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3332 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3334 if ( user_data->with_timestamps ) {
3335 if (!p1->has_timestamp || !p2->has_timestamp) {
3340 // Don't add tracks with timestamps when getting non timestamp tracks
3341 if (p1->has_timestamp || p2->has_timestamp) {
3347 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3350 /* called for each key in track hash table. if original track user_data[1] is close enough
3351 * to the passed one, add it to list in user_data[0]
3353 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3356 VikTrackpoint *p1, *p2;
3358 GList **nearby_tracks = ((gpointer *)user_data)[0];
3359 GList *orig_track = ((gpointer *)user_data)[1];
3360 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3363 * detect reasons for not merging, and return
3364 * if no reason is found not to merge, then do it.
3367 if (VIK_TRACK(value)->trackpoints == orig_track) {
3371 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3372 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3374 if (VIK_TRACK(value)->trackpoints) {
3375 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3376 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3378 if (!p1->has_timestamp || !p2->has_timestamp) {
3379 g_print("no timestamp\n");
3383 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3384 if (! (abs(t1 - p2->timestamp) < thr*60 ||
3386 abs(p1->timestamp - t2) < thr*60)
3393 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3396 /* comparison function used to sort tracks; a and b are hash table keys */
3397 /* Not actively used - can be restored if needed
3398 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3400 GHashTable *tracks = user_data;
3403 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3404 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3406 if (t1 < t2) return -1;
3407 if (t1 > t2) return 1;
3412 /* comparison function used to sort trackpoints */
3413 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3415 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3417 if (t1 < t2) return -1;
3418 if (t1 > t2) return 1;
3422 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3424 * comparison function which can be used to sort tracks or waypoints by name
3426 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3428 const gchar* namea = (const gchar*) a;
3429 const gchar* nameb = (const gchar*) b;
3430 if ( namea == NULL || nameb == NULL)
3433 // Same sort method as used in the vik_treeview_*_alphabetize functions
3434 return strcmp ( namea, nameb );
3439 * Attempt to merge selected track with other tracks specified by the user
3440 * Tracks to merge with must be of the same 'type' as the selected track -
3441 * either all with timestamps, or all without timestamps
3443 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3445 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3446 gchar *orig_track_name = pass_along[3];
3447 GList *other_tracks = NULL;
3448 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3450 if ( !track->trackpoints )
3454 udata.result = &other_tracks;
3455 udata.exclude = track->trackpoints;
3456 // Allow merging with 'similar' time type time tracks
3457 // i.e. either those times, or those without
3458 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3460 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3461 other_tracks = g_list_reverse(other_tracks);
3463 if ( !other_tracks ) {
3464 if ( udata.with_timestamps )
3465 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3467 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3471 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3472 // Sort alphabetically for user presentation
3473 other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3476 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3478 _("Merge with..."), _("Select track to merge with"));
3479 g_list_free(other_tracks);
3484 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3485 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3487 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3488 merge_track->trackpoints = NULL;
3489 vik_trw_layer_delete_track(vtl, l->data);
3490 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3493 /* TODO: free data before free merge_list */
3494 for (l = merge_list; l != NULL; l = g_list_next(l))
3496 g_list_free(merge_list);
3497 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3501 /* merge by time routine */
3502 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3504 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3505 gchar *orig_track_name = strdup(pass_along[3]);
3508 GList *nearby_tracks;
3511 static guint thr = 1;
3512 guint track_count = 0;
3514 GList *tracks_with_timestamp = NULL;
3515 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3516 if (track->trackpoints &&
3517 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3518 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3519 free(orig_track_name);
3524 udata.result = &tracks_with_timestamp;
3525 udata.exclude = track->trackpoints;
3526 udata.with_timestamps = TRUE;
3527 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3528 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3530 if (!tracks_with_timestamp) {
3531 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3532 free(orig_track_name);
3535 g_list_free(tracks_with_timestamp);
3537 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3538 _("Merge Threshold..."),
3539 _("Merge when time between tracks less than:"),
3541 free(orig_track_name);
3545 /* merge tracks until we can't */
3546 nearby_tracks = NULL;
3550 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3551 trps = track->trackpoints;
3556 if (nearby_tracks) {
3557 g_list_free(nearby_tracks);
3558 nearby_tracks = NULL;
3561 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3562 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3564 /* g_print("Original track times: %d and %d\n", t1, t2); */
3565 params[0] = &nearby_tracks;
3567 params[2] = GUINT_TO_POINTER (thr);
3569 /* get a list of adjacent-in-time tracks */
3570 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3572 /* add original track */
3573 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3577 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3578 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3579 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3580 GList *l = nearby_tracks;
3581 VikTrack *tr = vik_track_new();
3582 tr->visible = track->visible;
3587 t1 = get_first_trackpoint(l)->timestamp;
3588 t2 = get_last_trackpoint(l)->timestamp;
3589 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3593 /* remove trackpoints from merged track, delete track */
3594 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3595 get_track(l)->trackpoints = NULL;
3596 vik_trw_layer_delete_track(vtl, l->data);
3601 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3602 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3604 #undef get_first_trackpoint
3605 #undef get_last_trackpoint
3608 } while (track_count > 1);
3609 g_list_free(nearby_tracks);
3610 free(orig_track_name);
3611 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3614 /* split by time routine */
3615 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3617 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3618 GList *trps = track->trackpoints;
3620 GList *newlists = NULL;
3621 GList *newtps = NULL;
3623 static guint thr = 1;
3630 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3631 _("Split Threshold..."),
3632 _("Split when time between trackpoints exceeds:"),
3637 /* iterate through trackpoints, and copy them into new lists without touching original list */
3638 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3642 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3644 g_print("panic: ts < prev_ts: this should never happen!\n");
3647 if (ts - prev_ts > thr*60) {
3648 /* flush accumulated trackpoints into new list */
3649 newlists = g_list_append(newlists, g_list_reverse(newtps));
3653 /* accumulate trackpoint copies in newtps, in reverse order */
3654 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3656 iter = g_list_next(iter);
3659 newlists = g_list_append(newlists, g_list_reverse(newtps));
3662 /* put lists of trackpoints into tracks */
3665 // Only bother updating if the split results in new tracks
3666 if (g_list_length (newlists) > 1) {
3671 tr = vik_track_new();
3672 tr->visible = track->visible;
3673 tr->trackpoints = (GList *)(iter->data);
3675 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3676 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3677 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3678 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3680 iter = g_list_next(iter);
3682 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3683 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3685 g_list_free(newlists);
3689 * Split a track by the number of points as specified by the user
3691 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3693 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3694 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3696 // Check valid track
3697 GList *trps = track->trackpoints;
3701 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3702 _("Split Every Nth Point"),
3703 _("Split on every Nth point:"),
3704 250, // Default value as per typical limited track capacity of various GPS devices
3708 // Was a valid number returned?
3714 GList *newlists = NULL;
3715 GList *newtps = NULL;
3720 /* accumulate trackpoint copies in newtps, in reverse order */
3721 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3723 if (count >= points) {
3724 /* flush accumulated trackpoints into new list */
3725 newlists = g_list_append(newlists, g_list_reverse(newtps));
3729 iter = g_list_next(iter);
3732 // If there is a remaining chunk put that into the new split list
3733 // This may well be the whole track if no split points were encountered
3735 newlists = g_list_append(newlists, g_list_reverse(newtps));
3738 /* put lists of trackpoints into tracks */
3741 // Only bother updating if the split results in new tracks
3742 if (g_list_length (newlists) > 1) {
3747 tr = vik_track_new();
3748 tr->visible = track->visible;
3749 tr->trackpoints = (GList *)(iter->data);
3751 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3752 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3754 iter = g_list_next(iter);
3756 // Remove original track and then update the display
3757 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3758 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3760 g_list_free(newlists);
3763 /* end of split/merge routines */
3768 static void trw_layer_reverse ( gpointer pass_along[6] )
3770 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3771 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3773 // Check valid track
3774 GList *trps = track->trackpoints;
3778 vik_track_reverse ( track );
3780 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3784 * Similar to trw_layer_enum_item, but this uses a sorted method
3786 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3788 GList **list = (GList**)udata;
3789 //*list = g_list_prepend(*all, key); //unsorted method
3790 // Sort named list alphabetically
3791 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3797 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3799 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3801 // Sort list alphabetically for better presentation
3802 g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3805 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3809 // Get list of items to delete from the user
3810 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3813 _("Delete Selection"),
3814 _("Select tracks to delete"));
3817 // Delete requested tracks
3818 // since specificly requested, IMHO no need for extra confirmation
3819 if ( delete_list ) {
3821 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3822 vik_trw_layer_delete_track(vtl, l->data);
3824 g_list_free(delete_list);
3825 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3832 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3834 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3837 // Sort list alphabetically for better presentation
3838 g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3840 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3844 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3846 // Get list of items to delete from the user
3847 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3850 _("Delete Selection"),
3851 _("Select waypoints to delete"));
3854 // Delete requested waypoints
3855 // since specificly requested, IMHO no need for extra confirmation
3856 if ( delete_list ) {
3858 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3859 vik_trw_layer_delete_waypoint(vtl, l->data);
3861 g_list_free(delete_list);
3862 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3867 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3869 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3871 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3874 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3876 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3877 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3881 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3883 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3888 if (strcmp(newname, sublayer) == 0 )
3891 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3892 if (g_hash_table_lookup( l->waypoints, newname))
3894 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3899 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3900 g_hash_table_steal ( l->waypoints_iters, sublayer );
3902 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3903 highest_wp_number_remove_wp(l, sublayer);
3904 g_hash_table_remove ( l->waypoints, sublayer );
3906 rv = g_strdup(newname);
3908 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3910 highest_wp_number_add_wp(l, rv);
3911 g_hash_table_insert ( l->waypoints, rv, wp );
3912 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3914 /* it hasn't been updated yet so we pass new name */
3915 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3916 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3919 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3922 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3929 if (strcmp(newname, sublayer) == 0)
3932 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3933 if (g_hash_table_lookup( l->tracks, newname))
3935 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3940 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3941 g_hash_table_steal ( l->tracks, sublayer );
3943 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3944 g_hash_table_steal ( l->tracks_iters, sublayer );
3946 rv = g_strdup(newname);
3948 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3950 g_hash_table_insert ( l->tracks, rv, tr );
3951 g_hash_table_insert ( l->tracks_iters, rv, iter );
3953 /* don't forget about current_tp_track_name, update that too */
3954 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3956 l->current_tp_track_name = rv;
3958 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3960 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3961 l->last_tp_track_name = rv;
3963 g_free ( orig_key );
3965 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3966 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3969 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3975 static gboolean is_valid_geocache_name ( gchar *str )
3977 gint len = strlen ( str );
3978 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]));
3981 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3983 gchar *track_name = (gchar *) pass_along[3];
3984 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3985 a_acquire_set_filter_track ( tr, track_name );
3988 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3990 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3991 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3994 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3996 gchar *track_name = (gchar *) pass_along[3];
3997 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3999 gchar *escaped = uri_escape ( tr->comment );
4000 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4001 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4007 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4008 /* viewpoint is now available instead */
4009 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4011 static gpointer pass_along[6];
4013 gboolean rv = FALSE;
4016 pass_along[1] = vlp;
4017 pass_along[2] = GINT_TO_POINTER (subtype);
4018 pass_along[3] = sublayer;
4019 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
4020 pass_along[5] = vvp;
4022 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4026 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
4027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
4028 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4029 gtk_widget_show ( item );
4031 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4032 VikTrwLayer *vtl = l;
4033 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
4034 if (tr && tr->property_dialog)
4035 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
4038 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
4039 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
4040 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4041 gtk_widget_show ( item );
4043 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
4044 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
4045 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4046 gtk_widget_show ( item );
4048 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
4049 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
4050 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4051 gtk_widget_show ( item );
4053 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4055 gboolean separator_created = FALSE;
4057 /* could be a right-click using the tool */
4058 if ( vlp != NULL ) {
4059 item = gtk_menu_item_new ();
4060 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4061 gtk_widget_show ( item );
4063 separator_created = TRUE;
4065 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4068 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4069 gtk_widget_show ( item );
4072 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
4074 if ( !separator_created ) {
4075 item = gtk_menu_item_new ();
4076 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4077 gtk_widget_show ( item );
4078 separator_created = TRUE;
4081 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
4082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
4083 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4084 gtk_widget_show ( item );
4087 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4089 if ( wp && wp->image )
4091 if ( !separator_created ) {
4092 item = gtk_menu_item_new ();
4093 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4094 gtk_widget_show ( item );
4095 separator_created = TRUE;
4098 // Set up image paramater
4099 pass_along[5] = wp->image;
4101 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
4102 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
4103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
4104 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4105 gtk_widget_show ( item );
4107 #ifdef VIK_CONFIG_GEOTAG
4108 GtkWidget *geotag_submenu = gtk_menu_new ();
4109 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
4110 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
4111 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4112 gtk_widget_show ( item );
4113 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
4115 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
4116 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
4117 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4118 gtk_widget_show ( item );
4120 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
4121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
4122 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4123 gtk_widget_show ( item );
4130 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
4133 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
4134 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4136 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4137 gtk_widget_show ( item );
4140 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
4142 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
4143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4145 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4146 gtk_widget_show ( item );
4148 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4151 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4152 gtk_widget_show ( item );
4154 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
4155 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4156 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4157 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4158 gtk_widget_show ( item );
4160 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
4161 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4163 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4164 gtk_widget_show ( item );
4167 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
4171 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
4172 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4174 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4175 gtk_widget_show ( item );
4177 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
4178 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4180 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4181 gtk_widget_show ( item );
4183 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
4184 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4186 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4187 gtk_widget_show ( item );
4190 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4192 GtkWidget *goto_submenu;
4193 item = gtk_menu_item_new ();
4194 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4195 gtk_widget_show ( item );
4197 goto_submenu = gtk_menu_new ();
4198 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4199 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4200 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4201 gtk_widget_show ( item );
4202 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
4204 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
4205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
4206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
4207 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4208 gtk_widget_show ( item );
4210 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
4211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4212 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
4213 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4214 gtk_widget_show ( item );
4216 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
4217 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
4218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
4219 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4220 gtk_widget_show ( item );
4222 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
4223 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
4224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
4225 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4226 gtk_widget_show ( item );
4228 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4229 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4230 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4231 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4232 gtk_widget_show ( item );
4234 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4235 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4236 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4237 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4238 gtk_widget_show ( item );
4240 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4243 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4244 gtk_widget_show ( item );
4246 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4247 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4248 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4249 gtk_widget_show ( item );
4251 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4253 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4254 gtk_widget_show ( item );
4256 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4257 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4258 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4259 gtk_widget_show ( item );
4261 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4262 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4263 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4264 gtk_widget_show ( item );
4266 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
4267 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
4268 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
4269 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4270 gtk_widget_show ( item );
4272 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4274 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4275 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
4276 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4277 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4278 gtk_widget_show ( item );
4281 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4282 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
4283 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4284 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4285 gtk_widget_show ( item );
4287 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4288 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4289 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
4290 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4291 gtk_widget_show ( item );
4293 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
4294 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4296 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4297 gtk_widget_show ( item );
4299 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4300 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
4301 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4302 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4303 gtk_widget_show ( item );
4305 #ifdef VIK_CONFIG_OPENSTREETMAP
4306 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4307 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4308 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4309 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4310 gtk_widget_show ( item );
4313 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
4315 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4316 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4317 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4318 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4319 gtk_widget_show ( item );
4322 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4323 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4324 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4325 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4326 gtk_widget_show ( item );
4328 /* ATM This function is only available via the layers panel, due to needing a vlp */
4330 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4331 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4332 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4334 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4335 gtk_widget_show ( item );
4339 #ifdef VIK_CONFIG_GEOTAG
4340 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4341 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
4342 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4343 gtk_widget_show ( item );
4346 // Only show on viewport popmenu when a trackpoint is selected
4347 if ( ! vlp && l->current_tpl ) {
4349 item = gtk_menu_item_new ();
4350 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4351 gtk_widget_show ( item );
4353 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4354 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4355 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4356 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4357 gtk_widget_show ( item );
4365 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4368 if (!vtl->current_tpl)
4370 if (!vtl->current_tpl->next)
4373 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4374 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4376 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4379 VikTrackpoint *tp_new = vik_trackpoint_new();
4380 struct LatLon ll_current, ll_next;
4381 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4382 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4384 /* main positional interpolation */
4385 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4386 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4388 /* Now other properties that can be interpolated */
4389 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4391 if (tp_current->has_timestamp && tp_next->has_timestamp) {
4392 /* Note here the division is applied to each part, then added
4393 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4394 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4395 tp_new->has_timestamp = TRUE;
4398 if (tp_current->speed != NAN && tp_next->speed != NAN)
4399 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4401 /* TODO - improve interpolation of course, as it may not be correct.
4402 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4403 [similar applies if value is in radians] */
4404 if (tp_current->course != NAN && tp_next->course != NAN)
4405 tp_new->speed = (tp_current->course + tp_next->course)/2;
4407 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4409 /* Insert new point into the trackpoints list after the current TP */
4410 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4411 gint index = g_list_index ( tr->trackpoints, tp_current );
4413 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4418 /* to be called when last_tpl no long exists. */
4419 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4421 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4422 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4423 vtl->last_tpl = NULL;
4424 vtl->last_tp_track_name = NULL;
4427 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4433 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4437 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4439 if ( vtl->current_tpl )
4441 vtl->current_tpl = NULL;
4442 vtl->current_tp_track_name = NULL;
4443 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4447 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4449 g_assert ( vtl->tpwin != NULL );
4450 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4451 trw_layer_cancel_current_tp ( vtl, TRUE );
4453 if ( vtl->current_tpl == NULL )
4456 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4458 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4459 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4461 VikTrack *tr = vik_track_new ();
4462 GList *newglist = g_list_alloc ();
4463 newglist->prev = NULL;
4464 newglist->next = vtl->current_tpl->next;
4465 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4466 tr->trackpoints = newglist;
4468 vtl->current_tpl->next->prev = newglist; /* end old track here */
4469 vtl->current_tpl->next = NULL;
4471 vtl->current_tpl = newglist; /* change tp to first of new track. */
4472 vtl->current_tp_track_name = name;
4474 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4478 vik_trw_layer_add_track ( vtl, name, tr );
4479 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4482 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4484 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4486 g_assert(tr != NULL);
4488 /* can't join with a non-existent trackpoint */
4489 vtl->last_tpl = NULL;
4490 vtl->last_tp_track_name = NULL;
4492 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4494 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4495 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4497 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4499 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4500 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4502 trw_layer_cancel_last_tp ( vtl );
4504 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4505 g_list_free_1 ( vtl->current_tpl );
4506 vtl->current_tpl = new_tpl;
4507 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4511 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4512 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4513 g_list_free_1 ( vtl->current_tpl );
4514 trw_layer_cancel_current_tp ( vtl, FALSE );
4517 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4519 vtl->last_tpl = vtl->current_tpl;
4520 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4521 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4523 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4525 vtl->last_tpl = vtl->current_tpl;
4526 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4527 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4529 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4531 // Check tracks exist and are different before joining
4532 if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4535 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4536 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4538 VikTrack *tr_first = tr1, *tr_last = tr2;
4542 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4543 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4544 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4545 vik_track_reverse ( tr1 );
4546 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4551 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4553 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. */
4554 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4555 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4556 tr2->trackpoints = NULL;
4558 tmp = vtl->current_tp_track_name;
4560 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4561 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4563 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4564 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4565 vik_trw_layer_delete_track ( vtl, tmp );
4567 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4568 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4570 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4572 trw_layer_insert_tp_after_current_tp ( vtl );
4573 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4575 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4576 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4579 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4583 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4584 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4585 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4586 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4587 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4589 if ( vtl->current_tpl )
4590 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4591 /* set layer name and TP data */
4594 /***************************************************************************
4596 ***************************************************************************/
4598 /*** Utility data structures and functions ****/
4602 gint closest_x, closest_y;
4603 gchar *closest_wp_name;
4604 VikWaypoint *closest_wp;
4610 gint closest_x, closest_y;
4611 gchar *closest_track_name;
4612 VikTrackpoint *closest_tp;
4617 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4623 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4625 // If waypoint has an image then use the image size to select
4627 gint slackx, slacky;
4628 slackx = wp->image_width / 2;
4629 slacky = wp->image_height / 2;
4631 if ( x <= params->x + slackx && x >= params->x - slackx
4632 && y <= params->y + slacky && y >= params->y - slacky ) {
4633 params->closest_wp_name = name;
4634 params->closest_wp = wp;
4635 params->closest_x = x;
4636 params->closest_y = y;
4639 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4640 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
4641 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4643 params->closest_wp_name = name;
4644 params->closest_wp = wp;
4645 params->closest_x = x;
4646 params->closest_y = y;
4650 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4652 GList *tpl = t->trackpoints;
4661 tp = VIK_TRACKPOINT(tpl->data);
4663 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4665 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4666 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
4667 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4669 params->closest_track_name = name;
4670 params->closest_tp = tp;
4671 params->closest_tpl = tpl;
4672 params->closest_x = x;
4673 params->closest_y = y;
4679 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4681 TPSearchParams params;
4685 params.closest_track_name = NULL;
4686 params.closest_tp = NULL;
4687 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4688 return params.closest_tp;
4691 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4693 WPSearchParams params;
4697 params.closest_wp = NULL;
4698 params.closest_wp_name = NULL;
4699 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4700 return params.closest_wp;
4704 // Some forward declarations
4705 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4706 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4707 static void marker_end_move ( tool_ed_t *t );
4710 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4714 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4716 // Here always allow snapping back to the original location
4717 // this is useful when one decides not to move the thing afterall
4718 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4721 if ( event->state & GDK_CONTROL_MASK )
4723 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4725 new_coord = tp->coord;
4729 if ( event->state & GDK_SHIFT_MASK )
4731 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4733 new_coord = wp->coord;
4737 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4739 marker_moveto ( t, x, y );
4746 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4748 if ( t->holding && event->button == 1 )
4751 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4754 if ( event->state & GDK_CONTROL_MASK )
4756 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4758 new_coord = tp->coord;
4762 if ( event->state & GDK_SHIFT_MASK )
4764 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4766 new_coord = wp->coord;
4769 marker_end_move ( t );
4771 // Determine if working on a waypoint or a trackpoint
4772 if ( t->is_waypoint )
4773 vtl->current_wp->coord = new_coord;
4775 if ( vtl->current_tpl ) {
4776 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4779 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4781 // Don't really know what this is for but seems like it might be handy...
4782 /* can't join with itself! */
4783 trw_layer_cancel_last_tp ( vtl );
4788 vtl->current_wp = NULL;
4789 vtl->current_wp_name = NULL;
4790 trw_layer_cancel_current_tp ( vtl, FALSE );
4792 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4799 Returns true if a waypoint or track is found near the requested event position for this particular layer
4800 The item found is automatically selected
4801 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4803 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4805 if ( event->button != 1 )
4808 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4811 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4814 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4816 if (vtl->waypoints_visible) {
4817 WPSearchParams wp_params;
4818 wp_params.vvp = vvp;
4819 wp_params.x = event->x;
4820 wp_params.y = event->y;
4821 wp_params.closest_wp_name = NULL;
4822 wp_params.closest_wp = NULL;
4824 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4826 if ( wp_params.closest_wp ) {
4829 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4831 // Too easy to move it so must be holding shift to start immediately moving it
4832 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
4833 if ( event->state & GDK_SHIFT_MASK ||
4834 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
4835 // Put into 'move buffer'
4836 // NB vvp & vw already set in tet
4837 tet->vtl = (gpointer)vtl;
4838 tet->is_waypoint = TRUE;
4840 marker_begin_move (tet, event->x, event->y);
4843 vtl->current_wp = wp_params.closest_wp;
4844 vtl->current_wp_name = wp_params.closest_wp_name;
4846 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4852 if (vtl->tracks_visible) {
4853 TPSearchParams tp_params;
4854 tp_params.vvp = vvp;
4855 tp_params.x = event->x;
4856 tp_params.y = event->y;
4857 tp_params.closest_track_name = NULL;
4858 tp_params.closest_tp = NULL;
4860 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4862 if ( tp_params.closest_tp ) {
4864 // Always select + highlight the track
4865 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4867 tet->is_waypoint = FALSE;
4869 // Select the Trackpoint
4870 // Can move it immediately when control held or it's the previously selected tp
4871 if ( event->state & GDK_CONTROL_MASK ||
4872 vtl->current_tpl == tp_params.closest_tpl ) {
4873 // Put into 'move buffer'
4874 // NB vvp & vw already set in tet
4875 tet->vtl = (gpointer)vtl;
4876 marker_begin_move (tet, event->x, event->y);
4879 vtl->current_tpl = tp_params.closest_tpl;
4880 vtl->current_tp_track_name = tp_params.closest_track_name;
4882 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
4885 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4887 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4892 /* these aren't the droids you're looking for */
4893 vtl->current_wp = NULL;
4894 vtl->current_wp_name = NULL;
4895 trw_layer_cancel_current_tp ( vtl, FALSE );
4898 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
4903 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4905 if ( event->button != 3 )
4908 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4911 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4914 /* Post menu for the currently selected item */
4916 /* See if a track is selected */
4917 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4918 if ( track && track->visible ) {
4920 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4922 if ( vtl->track_right_click_menu )
4923 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4925 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4927 trw_layer_sublayer_add_menu_items ( vtl,
4928 vtl->track_right_click_menu,
4930 VIK_TRW_LAYER_SUBLAYER_TRACK,
4931 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4932 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4935 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4941 /* See if a waypoint is selected */
4942 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4943 if ( waypoint && waypoint->visible ) {
4944 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4946 if ( vtl->wp_right_click_menu )
4947 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4949 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4950 trw_layer_sublayer_add_menu_items ( vtl,
4951 vtl->wp_right_click_menu,
4953 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4954 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4955 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4957 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4966 /* background drawing hook, to be passed the viewport */
4967 static gboolean tool_sync_done = TRUE;
4969 static gboolean tool_sync(gpointer data)
4971 VikViewport *vvp = data;
4972 gdk_threads_enter();
4973 vik_viewport_sync(vvp);
4974 tool_sync_done = TRUE;
4975 gdk_threads_leave();
4979 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4982 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4983 gdk_gc_set_function ( t->gc, GDK_INVERT );
4984 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4985 vik_viewport_sync(t->vvp);
4990 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4992 VikViewport *vvp = t->vvp;
4993 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4994 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4998 if (tool_sync_done) {
4999 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
5000 tool_sync_done = FALSE;
5004 static void marker_end_move ( tool_ed_t *t )
5006 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5007 g_object_unref ( t->gc );
5011 /*** Edit waypoint ****/
5013 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5015 tool_ed_t *t = g_new(tool_ed_t, 1);
5021 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5023 WPSearchParams params;
5024 tool_ed_t *t = data;
5025 VikViewport *vvp = t->vvp;
5027 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5034 if ( !vtl->vl.visible || !vtl->waypoints_visible )
5037 if ( vtl->current_wp && vtl->current_wp->visible )
5039 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5041 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5043 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5044 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5046 if ( event->button == 3 )
5047 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5049 marker_begin_move(t, event->x, event->y);
5056 params.x = event->x;
5057 params.y = event->y;
5058 params.closest_wp_name = NULL;
5059 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5060 params.closest_wp = NULL;
5061 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5062 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5064 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5065 marker_begin_move(t, event->x, event->y);
5066 g_critical("shouldn't be here");
5069 else if ( params.closest_wp )
5071 if ( event->button == 3 )
5072 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5074 vtl->waypoint_rightclick = FALSE;
5076 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
5078 vtl->current_wp = params.closest_wp;
5079 vtl->current_wp_name = params.closest_wp_name;
5081 /* could make it so don't update if old WP is off screen and new is null but oh well */
5082 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5086 vtl->current_wp = NULL;
5087 vtl->current_wp_name = NULL;
5088 vtl->waypoint_rightclick = FALSE;
5089 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5093 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5095 tool_ed_t *t = data;
5096 VikViewport *vvp = t->vvp;
5098 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5103 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5106 if ( event->state & GDK_CONTROL_MASK )
5108 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5110 new_coord = tp->coord;
5114 if ( event->state & GDK_SHIFT_MASK )
5116 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5117 if ( wp && wp != vtl->current_wp )
5118 new_coord = wp->coord;
5123 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5125 marker_moveto ( t, x, y );
5132 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5134 tool_ed_t *t = data;
5135 VikViewport *vvp = t->vvp;
5137 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5140 if ( t->holding && event->button == 1 )
5143 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5146 if ( event->state & GDK_CONTROL_MASK )
5148 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5150 new_coord = tp->coord;
5154 if ( event->state & GDK_SHIFT_MASK )
5156 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5157 if ( wp && wp != vtl->current_wp )
5158 new_coord = wp->coord;
5161 marker_end_move ( t );
5163 vtl->current_wp->coord = new_coord;
5164 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5167 /* PUT IN RIGHT PLACE!!! */
5168 if ( event->button == 3 && vtl->waypoint_rightclick )
5170 if ( vtl->wp_right_click_menu )
5171 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5172 if ( vtl->current_wp ) {
5173 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5174 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 );
5175 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5177 vtl->waypoint_rightclick = FALSE;
5182 /**** Begin track ***/
5183 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5188 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5190 vtl->current_track = NULL;
5191 return tool_new_track_click ( vtl, event, vvp );
5194 /*** New track ****/
5196 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5204 gint x1,y1,x2,y2,x3,y3;
5206 } new_track_move_passalong_t;
5208 /* sync and undraw, but only when we have time */
5209 static gboolean ct_sync ( gpointer passalong )
5211 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5213 vik_viewport_sync ( p->vvp );
5214 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5215 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5216 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);
5217 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5219 g_free ( (gpointer) p->str ) ;
5220 p->vtl->ct_sync_done = TRUE;
5225 static const gchar* distance_string (gdouble distance)
5229 /* draw label with distance */
5230 vik_units_distance_t dist_units = a_vik_get_units_distance ();
5231 switch (dist_units) {
5232 case VIK_UNITS_DISTANCE_MILES:
5233 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5234 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5235 } else if (distance < 1609.4) {
5236 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5238 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5242 // VIK_UNITS_DISTANCE_KILOMETRES
5243 if (distance >= 1000 && distance < 100000) {
5244 g_sprintf(str, "%3.2f km", distance/1000.0);
5245 } else if (distance < 1000) {
5246 g_sprintf(str, "%d m", (int)distance);
5248 g_sprintf(str, "%d km", (int)distance/1000);
5252 return g_strdup (str);
5256 * Actually set the message in statusbar
5258 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5260 // Only show elevation data when track has some elevation properties
5261 gchar str_gain_loss[64];
5262 str_gain_loss[0] = '\0';
5264 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5265 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5266 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5268 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5271 // Write with full gain/loss information
5272 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5273 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5278 * Figure out what information should be set in the statusbar and then write it
5280 static void update_statusbar ( VikTrwLayer *vtl )
5282 // Get elevation data
5283 gdouble elev_gain, elev_loss;
5284 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5286 /* Find out actual distance of current track */
5287 gdouble distance = vik_track_get_length (vtl->current_track);
5288 const gchar *str = distance_string (distance);
5290 statusbar_write (str, elev_gain, elev_loss, vtl);
5292 g_free ((gpointer)str);
5296 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5298 /* if we haven't sync'ed yet, we don't have time to do more. */
5299 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5300 GList *iter = vtl->current_track->trackpoints;
5301 new_track_move_passalong_t *passalong;
5304 while ( iter->next )
5306 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5307 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5308 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5310 /* Find out actual distance of current track */
5311 gdouble distance = vik_track_get_length (vtl->current_track);
5313 // Now add distance to where the pointer is //
5316 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5317 vik_coord_to_latlon ( &coord, &ll );
5318 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5320 // Get elevation data
5321 gdouble elev_gain, elev_loss;
5322 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5324 // Adjust elevation data (if available) for the current pointer position
5326 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5327 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5328 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5329 // Adjust elevation of last track point
5330 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5332 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5335 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5339 const gchar *str = distance_string (distance);
5341 /* offset from cursor a bit */
5344 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5345 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5347 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5349 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5350 passalong->vtl = vtl;
5351 passalong->vvp = vvp;
5354 passalong->x2 = event->x;
5355 passalong->y2 = event->y;
5358 passalong->str = str;
5360 // Update statusbar with full gain/loss information
5361 statusbar_write (str, elev_gain, elev_loss, vtl);
5363 /* this will sync and undraw when we have time to */
5364 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5365 vtl->ct_sync_done = FALSE;
5366 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5368 return VIK_LAYER_TOOL_ACK;
5371 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5373 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5374 vtl->current_track = NULL;
5375 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5377 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5379 if ( vtl->current_track->trackpoints )
5381 GList *last = g_list_last(vtl->current_track->trackpoints);
5382 g_free ( last->data );
5383 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5386 update_statusbar ( vtl );
5388 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5394 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5398 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5401 if ( event->button == 3 && vtl->current_track )
5404 if ( vtl->current_track->trackpoints )
5406 GList *last = g_list_last(vtl->current_track->trackpoints);
5407 g_free ( last->data );
5408 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5410 update_statusbar ( vtl );
5412 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5416 if ( event->type == GDK_2BUTTON_PRESS )
5418 /* subtract last (duplicate from double click) tp then end */
5419 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5421 GList *last = g_list_last(vtl->current_track->trackpoints);
5422 g_free ( last->data );
5423 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5424 /* undo last, then end */
5425 vtl->current_track = NULL;
5427 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5431 if ( ! vtl->current_track )
5433 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5434 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5436 vtl->current_track = vik_track_new();
5437 vtl->current_track->visible = TRUE;
5438 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5440 /* incase it was created by begin track */
5441 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5446 tp = vik_trackpoint_new();
5447 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5449 /* snap to other TP */
5450 if ( event->state & GDK_CONTROL_MASK )
5452 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5454 tp->coord = other_tp->coord;
5457 tp->newsegment = FALSE;
5458 tp->has_timestamp = FALSE;
5460 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5461 /* Auto attempt to get elevation from DEM data (if it's available) */
5462 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5464 vtl->ct_x1 = vtl->ct_x2;
5465 vtl->ct_y1 = vtl->ct_y2;
5466 vtl->ct_x2 = event->x;
5467 vtl->ct_y2 = event->y;
5469 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5473 /*** New waypoint ****/
5475 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5480 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5483 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5485 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5486 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5487 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5492 /*** Edit trackpoint ****/
5494 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5496 tool_ed_t *t = g_new(tool_ed_t, 1);
5502 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5504 tool_ed_t *t = data;
5505 VikViewport *vvp = t->vvp;
5506 TPSearchParams params;
5507 /* OUTDATED DOCUMENTATION:
5508 find 5 pixel range on each side. then put these UTM, and a pointer
5509 to the winning track name (and maybe the winning track itself), and a
5510 pointer to the winning trackpoint, inside an array or struct. pass
5511 this along, do a foreach on the tracks which will do a foreach on the
5514 params.x = event->x;
5515 params.y = event->y;
5516 params.closest_track_name = NULL;
5517 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5518 params.closest_tp = NULL;
5520 if ( event->button != 1 )
5523 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5526 if ( !vtl->vl.visible || !vtl->tracks_visible )
5529 if ( vtl->current_tpl )
5531 /* 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.) */
5532 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5533 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5535 g_assert ( current_tr );
5537 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5539 if ( current_tr->visible &&
5540 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5541 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5542 marker_begin_move ( t, event->x, event->y );
5546 vtl->last_tpl = vtl->current_tpl;
5547 vtl->last_tp_track_name = vtl->current_tp_track_name;
5550 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5552 if ( params.closest_tp )
5554 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5555 vtl->current_tpl = params.closest_tpl;
5556 vtl->current_tp_track_name = params.closest_track_name;
5557 trw_layer_tpwin_init ( vtl );
5558 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5559 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5563 /* these aren't the droids you're looking for */
5567 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5569 tool_ed_t *t = data;
5570 VikViewport *vvp = t->vvp;
5572 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5578 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5581 if ( event->state & GDK_CONTROL_MASK )
5583 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5584 if ( tp && tp != vtl->current_tpl->data )
5585 new_coord = tp->coord;
5587 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5590 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5591 marker_moveto ( t, x, y );
5599 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5601 tool_ed_t *t = data;
5602 VikViewport *vvp = t->vvp;
5604 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5606 if ( event->button != 1)
5611 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5614 if ( event->state & GDK_CONTROL_MASK )
5616 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5617 if ( tp && tp != vtl->current_tpl->data )
5618 new_coord = tp->coord;
5621 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5623 marker_end_move ( t );
5625 /* diff dist is diff from orig */
5627 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5628 /* can't join with itself! */
5629 trw_layer_cancel_last_tp ( vtl );
5631 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5638 /*** Route Finder ***/
5639 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5644 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5647 if ( !vtl ) return FALSE;
5648 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5649 if ( event->button == 3 && vtl->route_finder_current_track ) {
5651 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5653 vtl->route_finder_coord = *new_end;
5655 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5656 /* remove last ' to:...' */
5657 if ( vtl->route_finder_current_track->comment ) {
5658 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5659 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5660 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5661 last_to - vtl->route_finder_current_track->comment - 1);
5662 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5667 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5668 struct LatLon start, end;
5669 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5670 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5673 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5674 vik_coord_to_latlon ( &(tmp), &end );
5675 vtl->route_finder_coord = tmp; /* for continuations */
5677 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5678 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5679 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
5681 vtl->route_finder_check_added_track = TRUE;
5682 vtl->route_finder_started = FALSE;
5685 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5686 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5687 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5688 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5689 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5690 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5693 /* see if anything was done -- a track was added or appended to */
5694 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5697 tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5700 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5702 vtl->route_finder_current_track = tr;
5704 g_free ( vtl->route_finder_added_track_name );
5705 vtl->route_finder_added_track_name = NULL;
5706 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5707 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5708 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5709 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5711 vtl->route_finder_check_added_track = FALSE;
5712 vtl->route_finder_append = FALSE;
5714 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5716 vtl->route_finder_started = TRUE;
5717 vtl->route_finder_coord = tmp;
5718 vtl->route_finder_current_track = NULL;
5723 /*** Show picture ****/
5725 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5730 /* Params are: vvp, event, last match found or NULL */
5731 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
5733 if ( wp->image && wp->visible )
5735 gint x, y, slackx, slacky;
5736 GdkEventButton *event = (GdkEventButton *) params[1];
5738 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5739 slackx = wp->image_width / 2;
5740 slacky = wp->image_height / 2;
5741 if ( x <= event->x + slackx && x >= event->x - slackx
5742 && y <= event->y + slacky && y >= event->y - slacky )
5744 params[2] = wp->image; /* we've found a match. however continue searching
5745 * since we want to find the last match -- that
5746 * is, the match that was drawn last. */
5751 static void trw_layer_show_picture ( gpointer pass_along[6] )
5753 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5755 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5758 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5759 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
5760 g_free ( quoted_file );
5761 if ( ! g_spawn_command_line_async ( cmd, &err ) )
5763 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
5764 g_error_free ( err );
5767 #endif /* WINDOWS */
5770 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5772 gpointer params[3] = { vvp, event, NULL };
5773 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5775 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5778 static gpointer pass_along[6];
5779 pass_along[0] = vtl;
5780 pass_along[5] = params[2];
5781 trw_layer_show_picture ( pass_along );
5782 return TRUE; /* found a match */
5785 return FALSE; /* go through other layers, searching for a match */
5788 /***************************************************************************
5790 ***************************************************************************/
5796 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5798 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5799 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5802 /* Structure for thumbnail creating data used in the background thread */
5804 VikTrwLayer *vtl; // Layer needed for redrawing
5805 GSList *pics; // Image list
5806 } thumbnail_create_thread_data;
5808 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5810 guint total = g_slist_length(tctd->pics), done = 0;
5811 while ( tctd->pics )
5813 a_thumbnails_create ( (gchar *) tctd->pics->data );
5814 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5816 return -1; /* Abort thread */
5818 tctd->pics = tctd->pics->next;
5821 // Redraw to show the thumbnails as they are now created
5822 if ( IS_VIK_LAYER(tctd->vtl) )
5823 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
5828 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5830 while ( tctd->pics )
5832 g_free ( tctd->pics->data );
5833 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5838 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5840 if ( ! vtl->has_verified_thumbnails )
5842 GSList *pics = NULL;
5843 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5846 gint len = g_slist_length ( pics );
5847 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5848 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5851 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5853 (vik_thr_func) create_thumbnails_thread,
5855 (vik_thr_free_func) thumbnail_create_thread_free,
5863 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5865 return vtl->coord_mode;
5870 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5872 vik_coord_convert ( &(wp->coord), *dest_mode );
5875 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5877 vik_track_convert ( tr, *dest_mode );
5880 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5882 if ( vtl->coord_mode != dest_mode )
5884 vtl->coord_mode = dest_mode;
5885 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5886 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5890 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5892 return g_hash_table_lookup ( vtl->waypoints, name );
5895 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5897 return g_hash_table_lookup ( vtl->tracks, name );
5900 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5902 vtl->menu_selection = selection;
5905 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5907 return (vtl->menu_selection);
5910 /* ----------- Downloading maps along tracks --------------- */
5912 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5914 /* TODO: calculating based on current size of viewport */
5915 const gdouble w_at_zoom_0_125 = 0.0013;
5916 const gdouble h_at_zoom_0_125 = 0.0011;
5917 gdouble zoom_factor = zoom_level/0.125;
5919 wh->lat = h_at_zoom_0_125 * zoom_factor;
5920 wh->lon = w_at_zoom_0_125 * zoom_factor;
5922 return 0; /* all OK */
5925 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5927 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5928 (dist->lat >= ABS(to->north_south - from->north_south)))
5931 VikCoord *coord = g_malloc(sizeof(VikCoord));
5932 coord->mode = VIK_COORD_LATLON;
5934 if (ABS(gradient) < 1) {
5935 if (from->east_west > to->east_west)
5936 coord->east_west = from->east_west - dist->lon;
5938 coord->east_west = from->east_west + dist->lon;
5939 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5941 if (from->north_south > to->north_south)
5942 coord->north_south = from->north_south - dist->lat;
5944 coord->north_south = from->north_south + dist->lat;
5945 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5951 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5953 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5954 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5956 VikCoord *next = from;
5958 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5960 list = g_list_prepend(list, next);
5966 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5968 typedef struct _Rect {
5973 #define GLRECT(iter) ((Rect *)((iter)->data))
5976 GList *rects_to_download = NULL;
5979 if (get_download_area_width(vvp, zoom_level, &wh))
5982 GList *iter = tr->trackpoints;
5986 gboolean new_map = TRUE;
5987 VikCoord *cur_coord, tl, br;
5990 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5992 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5993 rect = g_malloc(sizeof(Rect));
5996 rect->center = *cur_coord;
5997 rects_to_download = g_list_prepend(rects_to_download, rect);
6002 gboolean found = FALSE;
6003 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6004 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
6015 GList *fillins = NULL;
6016 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
6017 /* seems that ATM the function get_next_coord works only for LATLON */
6018 if ( cur_coord->mode == VIK_COORD_LATLON ) {
6019 /* fill-ins for far apart points */
6020 GList *cur_rect, *next_rect;
6021 for (cur_rect = rects_to_download;
6022 (next_rect = cur_rect->next) != NULL;
6023 cur_rect = cur_rect->next) {
6024 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
6025 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
6026 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
6030 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
6033 GList *iter = fillins;
6035 cur_coord = (VikCoord *)(iter->data);
6036 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6037 rect = g_malloc(sizeof(Rect));
6040 rect->center = *cur_coord;
6041 rects_to_download = g_list_prepend(rects_to_download, rect);
6046 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6047 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
6051 for (iter = fillins; iter; iter = iter->next)
6053 g_list_free(fillins);
6055 if (rects_to_download) {
6056 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
6057 g_free(rect_iter->data);
6058 g_list_free(rects_to_download);
6062 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6065 gint selected_map, default_map;
6066 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6067 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6068 gint selected_zoom, default_zoom;
6072 VikTrwLayer *vtl = pass_along[0];
6073 VikLayersPanel *vlp = pass_along[1];
6074 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6075 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6077 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6078 int num_maps = g_list_length(vmls);
6081 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6085 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6086 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6088 gchar **np = map_names;
6089 VikMapsLayer **lp = map_layers;
6090 for (i = 0; i < num_maps; i++) {
6091 gboolean dup = FALSE;
6092 vml = (VikMapsLayer *)(vmls->data);
6093 for (j = 0; j < i; j++) { /* no duplicate allowed */
6094 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6101 *np++ = vik_maps_layer_get_map_label(vml);
6107 num_maps = lp - map_layers;
6109 for (default_map = 0; default_map < num_maps; default_map++) {
6110 /* TODO: check for parent layer's visibility */
6111 if (VIK_LAYER(map_layers[default_map])->visible)
6114 default_map = (default_map == num_maps) ? 0 : default_map;
6116 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6117 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6118 if (cur_zoom == zoom_vals[default_zoom])
6121 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6123 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6126 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6129 for (i = 0; i < num_maps; i++)
6130 g_free(map_names[i]);
6138 /**** lowest waypoint number calculation ***/
6139 static gint highest_wp_number_name_to_number(const gchar *name) {
6140 if ( strlen(name) == 3 ) {
6142 if ( n < 100 && name[0] != '0' )
6144 if ( n < 10 && name[0] != '0' )
6152 static void highest_wp_number_reset(VikTrwLayer *vtl)
6154 vtl->highest_wp_number = -1;
6157 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6159 /* if is bigger that top, add it */
6160 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6161 if ( new_wp_num > vtl->highest_wp_number )
6162 vtl->highest_wp_number = new_wp_num;
6165 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6167 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6168 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6169 if ( vtl->highest_wp_number == old_wp_num ) {
6171 vtl->highest_wp_number --;
6173 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6174 /* search down until we find something that *does* exist */
6176 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
6177 vtl->highest_wp_number --;
6178 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6183 /* get lowest unused number */
6184 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6187 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6189 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6190 return g_strdup(buf);