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;
3423 * comparison function which can be used to sort tracks or waypoints by name
3425 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3427 const gchar* namea = (const gchar*) a;
3428 const gchar* nameb = (const gchar*) b;
3429 if ( namea == NULL || nameb == NULL)
3432 // Same sort method as used in the vik_treeview_*_alphabetize functions
3433 return strcmp ( namea, nameb );
3437 * Attempt to merge selected track with other tracks specified by the user
3438 * Tracks to merge with must be of the same 'type' as the selected track -
3439 * either all with timestamps, or all without timestamps
3441 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3443 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3444 gchar *orig_track_name = pass_along[3];
3445 GList *other_tracks = NULL;
3446 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3448 if ( !track->trackpoints )
3452 udata.result = &other_tracks;
3453 udata.exclude = track->trackpoints;
3454 // Allow merging with 'similar' time type time tracks
3455 // i.e. either those times, or those without
3456 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3458 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3459 other_tracks = g_list_reverse(other_tracks);
3461 if ( !other_tracks ) {
3462 if ( udata.with_timestamps )
3463 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3465 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3469 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3470 // Sort alphabetically for user presentation
3471 other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3474 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3476 _("Merge with..."), _("Select track to merge with"));
3477 g_list_free(other_tracks);
3482 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3483 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3485 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3486 merge_track->trackpoints = NULL;
3487 vik_trw_layer_delete_track(vtl, l->data);
3488 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3491 /* TODO: free data before free merge_list */
3492 for (l = merge_list; l != NULL; l = g_list_next(l))
3494 g_list_free(merge_list);
3495 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3499 /* merge by time routine */
3500 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3502 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3503 gchar *orig_track_name = strdup(pass_along[3]);
3506 GList *nearby_tracks;
3509 static guint thr = 1;
3510 guint track_count = 0;
3512 GList *tracks_with_timestamp = NULL;
3513 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3514 if (track->trackpoints &&
3515 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3516 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3517 free(orig_track_name);
3522 udata.result = &tracks_with_timestamp;
3523 udata.exclude = track->trackpoints;
3524 udata.with_timestamps = TRUE;
3525 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3526 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3528 if (!tracks_with_timestamp) {
3529 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3530 free(orig_track_name);
3533 g_list_free(tracks_with_timestamp);
3535 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3536 _("Merge Threshold..."),
3537 _("Merge when time between tracks less than:"),
3539 free(orig_track_name);
3543 /* merge tracks until we can't */
3544 nearby_tracks = NULL;
3548 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3549 trps = track->trackpoints;
3554 if (nearby_tracks) {
3555 g_list_free(nearby_tracks);
3556 nearby_tracks = NULL;
3559 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3560 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3562 /* g_print("Original track times: %d and %d\n", t1, t2); */
3563 params[0] = &nearby_tracks;
3565 params[2] = GUINT_TO_POINTER (thr);
3567 /* get a list of adjacent-in-time tracks */
3568 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3570 /* add original track */
3571 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3575 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3576 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3577 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3578 GList *l = nearby_tracks;
3579 VikTrack *tr = vik_track_new();
3580 tr->visible = track->visible;
3585 t1 = get_first_trackpoint(l)->timestamp;
3586 t2 = get_last_trackpoint(l)->timestamp;
3587 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3591 /* remove trackpoints from merged track, delete track */
3592 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3593 get_track(l)->trackpoints = NULL;
3594 vik_trw_layer_delete_track(vtl, l->data);
3599 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3600 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3602 #undef get_first_trackpoint
3603 #undef get_last_trackpoint
3606 } while (track_count > 1);
3607 g_list_free(nearby_tracks);
3608 free(orig_track_name);
3609 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3612 /* split by time routine */
3613 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3615 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3616 GList *trps = track->trackpoints;
3618 GList *newlists = NULL;
3619 GList *newtps = NULL;
3621 static guint thr = 1;
3628 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3629 _("Split Threshold..."),
3630 _("Split when time between trackpoints exceeds:"),
3635 /* iterate through trackpoints, and copy them into new lists without touching original list */
3636 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3640 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3642 g_print("panic: ts < prev_ts: this should never happen!\n");
3645 if (ts - prev_ts > thr*60) {
3646 /* flush accumulated trackpoints into new list */
3647 newlists = g_list_append(newlists, g_list_reverse(newtps));
3651 /* accumulate trackpoint copies in newtps, in reverse order */
3652 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3654 iter = g_list_next(iter);
3657 newlists = g_list_append(newlists, g_list_reverse(newtps));
3660 /* put lists of trackpoints into tracks */
3663 // Only bother updating if the split results in new tracks
3664 if (g_list_length (newlists) > 1) {
3669 tr = vik_track_new();
3670 tr->visible = track->visible;
3671 tr->trackpoints = (GList *)(iter->data);
3673 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3674 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3675 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3676 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3678 iter = g_list_next(iter);
3680 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3681 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3683 g_list_free(newlists);
3687 * Split a track by the number of points as specified by the user
3689 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3691 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3692 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3694 // Check valid track
3695 GList *trps = track->trackpoints;
3699 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3700 _("Split Every Nth Point"),
3701 _("Split on every Nth point:"),
3702 250, // Default value as per typical limited track capacity of various GPS devices
3706 // Was a valid number returned?
3712 GList *newlists = NULL;
3713 GList *newtps = NULL;
3718 /* accumulate trackpoint copies in newtps, in reverse order */
3719 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3721 if (count >= points) {
3722 /* flush accumulated trackpoints into new list */
3723 newlists = g_list_append(newlists, g_list_reverse(newtps));
3727 iter = g_list_next(iter);
3730 // If there is a remaining chunk put that into the new split list
3731 // This may well be the whole track if no split points were encountered
3733 newlists = g_list_append(newlists, g_list_reverse(newtps));
3736 /* put lists of trackpoints into tracks */
3739 // Only bother updating if the split results in new tracks
3740 if (g_list_length (newlists) > 1) {
3745 tr = vik_track_new();
3746 tr->visible = track->visible;
3747 tr->trackpoints = (GList *)(iter->data);
3749 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3750 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3752 iter = g_list_next(iter);
3754 // Remove original track and then update the display
3755 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3756 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3758 g_list_free(newlists);
3761 /* end of split/merge routines */
3766 static void trw_layer_reverse ( gpointer pass_along[6] )
3768 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3769 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3771 // Check valid track
3772 GList *trps = track->trackpoints;
3776 vik_track_reverse ( track );
3778 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3782 * Similar to trw_layer_enum_item, but this uses a sorted method
3784 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3786 GList **list = (GList**)udata;
3787 //*list = g_list_prepend(*all, key); //unsorted method
3788 // Sort named list alphabetically
3789 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3795 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3797 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3799 // Sort list alphabetically for better presentation
3800 g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3803 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3807 // Get list of items to delete from the user
3808 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3811 _("Delete Selection"),
3812 _("Select tracks to delete"));
3815 // Delete requested tracks
3816 // since specificly requested, IMHO no need for extra confirmation
3817 if ( delete_list ) {
3819 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3820 vik_trw_layer_delete_track(vtl, l->data);
3822 g_list_free(delete_list);
3823 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3830 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3832 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3835 // Sort list alphabetically for better presentation
3836 g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3838 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3842 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3844 // Get list of items to delete from the user
3845 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3848 _("Delete Selection"),
3849 _("Select waypoints to delete"));
3852 // Delete requested waypoints
3853 // since specificly requested, IMHO no need for extra confirmation
3854 if ( delete_list ) {
3856 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3857 vik_trw_layer_delete_waypoint(vtl, l->data);
3859 g_list_free(delete_list);
3860 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3865 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3867 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3869 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3872 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3874 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3875 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3879 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3881 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3886 if (strcmp(newname, sublayer) == 0 )
3889 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3890 if (g_hash_table_lookup( l->waypoints, newname))
3892 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3897 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3898 g_hash_table_steal ( l->waypoints_iters, sublayer );
3900 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3901 highest_wp_number_remove_wp(l, sublayer);
3902 g_hash_table_remove ( l->waypoints, sublayer );
3904 rv = g_strdup(newname);
3906 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3908 highest_wp_number_add_wp(l, rv);
3909 g_hash_table_insert ( l->waypoints, rv, wp );
3910 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3912 /* it hasn't been updated yet so we pass new name */
3913 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3914 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3917 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3920 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3927 if (strcmp(newname, sublayer) == 0)
3930 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3931 if (g_hash_table_lookup( l->tracks, newname))
3933 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3938 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3939 g_hash_table_steal ( l->tracks, sublayer );
3941 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3942 g_hash_table_steal ( l->tracks_iters, sublayer );
3944 rv = g_strdup(newname);
3946 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3948 g_hash_table_insert ( l->tracks, rv, tr );
3949 g_hash_table_insert ( l->tracks_iters, rv, iter );
3951 /* don't forget about current_tp_track_name, update that too */
3952 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3954 l->current_tp_track_name = rv;
3956 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3958 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3959 l->last_tp_track_name = rv;
3961 g_free ( orig_key );
3963 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3964 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3967 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3973 static gboolean is_valid_geocache_name ( gchar *str )
3975 gint len = strlen ( str );
3976 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]));
3979 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3981 gchar *track_name = (gchar *) pass_along[3];
3982 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3983 a_acquire_set_filter_track ( tr, track_name );
3986 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3988 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3989 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3992 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3994 gchar *track_name = (gchar *) pass_along[3];
3995 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3997 gchar *escaped = uri_escape ( tr->comment );
3998 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3999 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4005 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4006 /* viewpoint is now available instead */
4007 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4009 static gpointer pass_along[6];
4011 gboolean rv = FALSE;
4014 pass_along[1] = vlp;
4015 pass_along[2] = GINT_TO_POINTER (subtype);
4016 pass_along[3] = sublayer;
4017 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
4018 pass_along[5] = vvp;
4020 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4024 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
4025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
4026 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4027 gtk_widget_show ( item );
4029 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4030 VikTrwLayer *vtl = l;
4031 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
4032 if (tr && tr->property_dialog)
4033 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
4036 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
4037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
4038 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4039 gtk_widget_show ( item );
4041 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
4042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
4043 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4044 gtk_widget_show ( item );
4046 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
4047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
4048 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4049 gtk_widget_show ( item );
4051 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4053 gboolean separator_created = FALSE;
4055 /* could be a right-click using the tool */
4056 if ( vlp != NULL ) {
4057 item = gtk_menu_item_new ();
4058 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4059 gtk_widget_show ( item );
4061 separator_created = TRUE;
4063 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4064 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4065 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4066 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4067 gtk_widget_show ( item );
4070 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
4072 if ( !separator_created ) {
4073 item = gtk_menu_item_new ();
4074 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4075 gtk_widget_show ( item );
4076 separator_created = TRUE;
4079 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
4080 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
4081 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4082 gtk_widget_show ( item );
4085 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4087 if ( wp && wp->image )
4089 if ( !separator_created ) {
4090 item = gtk_menu_item_new ();
4091 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4092 gtk_widget_show ( item );
4093 separator_created = TRUE;
4096 // Set up image paramater
4097 pass_along[5] = wp->image;
4099 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
4100 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
4101 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
4102 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4103 gtk_widget_show ( item );
4105 #ifdef VIK_CONFIG_GEOTAG
4106 GtkWidget *geotag_submenu = gtk_menu_new ();
4107 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
4108 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
4109 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4110 gtk_widget_show ( item );
4111 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
4113 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
4114 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
4115 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4116 gtk_widget_show ( item );
4118 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
4119 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
4120 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4121 gtk_widget_show ( item );
4128 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
4131 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
4132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4134 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4135 gtk_widget_show ( item );
4138 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
4140 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
4141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4143 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4144 gtk_widget_show ( item );
4146 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4147 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4148 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4149 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4150 gtk_widget_show ( item );
4152 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
4153 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4155 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4156 gtk_widget_show ( item );
4158 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
4159 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4161 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4162 gtk_widget_show ( item );
4165 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
4169 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
4170 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4172 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4173 gtk_widget_show ( item );
4175 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
4176 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4177 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4178 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4179 gtk_widget_show ( item );
4181 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
4182 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4183 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4184 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4185 gtk_widget_show ( item );
4188 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4190 GtkWidget *goto_submenu;
4191 item = gtk_menu_item_new ();
4192 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4193 gtk_widget_show ( item );
4195 goto_submenu = gtk_menu_new ();
4196 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4197 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4198 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4199 gtk_widget_show ( item );
4200 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
4202 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
4203 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
4204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
4205 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4206 gtk_widget_show ( item );
4208 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
4209 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4210 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
4211 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4212 gtk_widget_show ( item );
4214 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
4215 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
4216 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
4217 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4218 gtk_widget_show ( item );
4220 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
4221 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
4222 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
4223 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4224 gtk_widget_show ( item );
4226 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4227 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4228 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4229 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4230 gtk_widget_show ( item );
4232 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4233 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4234 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4235 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4236 gtk_widget_show ( item );
4238 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4239 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4240 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4241 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4242 gtk_widget_show ( item );
4244 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4246 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4247 gtk_widget_show ( item );
4249 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4250 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4251 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4252 gtk_widget_show ( item );
4254 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4257 gtk_widget_show ( item );
4259 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4261 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4262 gtk_widget_show ( item );
4264 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
4265 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
4266 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
4267 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4268 gtk_widget_show ( item );
4270 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4272 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4273 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
4274 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4275 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4276 gtk_widget_show ( item );
4279 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4280 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
4281 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4282 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4283 gtk_widget_show ( item );
4285 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4286 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4287 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
4288 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4289 gtk_widget_show ( item );
4291 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
4292 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4294 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4295 gtk_widget_show ( item );
4297 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4298 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
4299 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4300 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4301 gtk_widget_show ( item );
4303 #ifdef VIK_CONFIG_OPENSTREETMAP
4304 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4305 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4306 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4307 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4308 gtk_widget_show ( item );
4311 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
4313 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4314 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4316 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4317 gtk_widget_show ( item );
4320 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4321 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4323 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4324 gtk_widget_show ( item );
4326 /* ATM This function is only available via the layers panel, due to needing a vlp */
4328 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4329 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4330 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4332 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4333 gtk_widget_show ( item );
4337 #ifdef VIK_CONFIG_GEOTAG
4338 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
4340 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4341 gtk_widget_show ( item );
4344 // Only show on viewport popmenu when a trackpoint is selected
4345 if ( ! vlp && l->current_tpl ) {
4347 item = gtk_menu_item_new ();
4348 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4349 gtk_widget_show ( item );
4351 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4352 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4353 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4354 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4355 gtk_widget_show ( item );
4363 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4366 if (!vtl->current_tpl)
4368 if (!vtl->current_tpl->next)
4371 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4372 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4374 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4377 VikTrackpoint *tp_new = vik_trackpoint_new();
4378 struct LatLon ll_current, ll_next;
4379 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4380 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4382 /* main positional interpolation */
4383 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4384 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4386 /* Now other properties that can be interpolated */
4387 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4389 if (tp_current->has_timestamp && tp_next->has_timestamp) {
4390 /* Note here the division is applied to each part, then added
4391 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4392 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4393 tp_new->has_timestamp = TRUE;
4396 if (tp_current->speed != NAN && tp_next->speed != NAN)
4397 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4399 /* TODO - improve interpolation of course, as it may not be correct.
4400 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4401 [similar applies if value is in radians] */
4402 if (tp_current->course != NAN && tp_next->course != NAN)
4403 tp_new->speed = (tp_current->course + tp_next->course)/2;
4405 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4407 /* Insert new point into the trackpoints list after the current TP */
4408 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4409 gint index = g_list_index ( tr->trackpoints, tp_current );
4411 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4416 /* to be called when last_tpl no long exists. */
4417 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4419 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4420 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4421 vtl->last_tpl = NULL;
4422 vtl->last_tp_track_name = NULL;
4425 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4431 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4435 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4437 if ( vtl->current_tpl )
4439 vtl->current_tpl = NULL;
4440 vtl->current_tp_track_name = NULL;
4441 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4445 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4447 g_assert ( vtl->tpwin != NULL );
4448 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4449 trw_layer_cancel_current_tp ( vtl, TRUE );
4451 if ( vtl->current_tpl == NULL )
4454 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4456 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4457 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4459 VikTrack *tr = vik_track_new ();
4460 GList *newglist = g_list_alloc ();
4461 newglist->prev = NULL;
4462 newglist->next = vtl->current_tpl->next;
4463 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4464 tr->trackpoints = newglist;
4466 vtl->current_tpl->next->prev = newglist; /* end old track here */
4467 vtl->current_tpl->next = NULL;
4469 vtl->current_tpl = newglist; /* change tp to first of new track. */
4470 vtl->current_tp_track_name = name;
4472 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4476 vik_trw_layer_add_track ( vtl, name, tr );
4477 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4480 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4482 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4484 g_assert(tr != NULL);
4486 /* can't join with a non-existent trackpoint */
4487 vtl->last_tpl = NULL;
4488 vtl->last_tp_track_name = NULL;
4490 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4492 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4493 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4495 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4497 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4498 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4500 trw_layer_cancel_last_tp ( vtl );
4502 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4503 g_list_free_1 ( vtl->current_tpl );
4504 vtl->current_tpl = new_tpl;
4505 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4509 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4510 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4511 g_list_free_1 ( vtl->current_tpl );
4512 trw_layer_cancel_current_tp ( vtl, FALSE );
4515 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4517 vtl->last_tpl = vtl->current_tpl;
4518 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4519 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4521 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4523 vtl->last_tpl = vtl->current_tpl;
4524 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4525 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4527 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4529 // Check tracks exist and are different before joining
4530 if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4533 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4534 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4536 VikTrack *tr_first = tr1, *tr_last = tr2;
4540 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4541 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4542 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4543 vik_track_reverse ( tr1 );
4544 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4549 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4551 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. */
4552 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4553 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4554 tr2->trackpoints = NULL;
4556 tmp = vtl->current_tp_track_name;
4558 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4559 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4561 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4562 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4563 vik_trw_layer_delete_track ( vtl, tmp );
4565 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4566 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4568 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4570 trw_layer_insert_tp_after_current_tp ( vtl );
4571 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4573 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4574 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4577 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4581 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4582 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4583 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4584 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4585 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4587 if ( vtl->current_tpl )
4588 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4589 /* set layer name and TP data */
4592 /***************************************************************************
4594 ***************************************************************************/
4596 /*** Utility data structures and functions ****/
4600 gint closest_x, closest_y;
4601 gchar *closest_wp_name;
4602 VikWaypoint *closest_wp;
4608 gint closest_x, closest_y;
4609 gchar *closest_track_name;
4610 VikTrackpoint *closest_tp;
4615 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4621 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4623 // If waypoint has an image then use the image size to select
4625 gint slackx, slacky;
4626 slackx = wp->image_width / 2;
4627 slacky = wp->image_height / 2;
4629 if ( x <= params->x + slackx && x >= params->x - slackx
4630 && y <= params->y + slacky && y >= params->y - slacky ) {
4631 params->closest_wp_name = name;
4632 params->closest_wp = wp;
4633 params->closest_x = x;
4634 params->closest_y = y;
4637 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4638 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
4639 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4641 params->closest_wp_name = name;
4642 params->closest_wp = wp;
4643 params->closest_x = x;
4644 params->closest_y = y;
4648 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4650 GList *tpl = t->trackpoints;
4659 tp = VIK_TRACKPOINT(tpl->data);
4661 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4663 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4664 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
4665 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4667 params->closest_track_name = name;
4668 params->closest_tp = tp;
4669 params->closest_tpl = tpl;
4670 params->closest_x = x;
4671 params->closest_y = y;
4677 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4679 TPSearchParams params;
4683 params.closest_track_name = NULL;
4684 params.closest_tp = NULL;
4685 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4686 return params.closest_tp;
4689 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4691 WPSearchParams params;
4695 params.closest_wp = NULL;
4696 params.closest_wp_name = NULL;
4697 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4698 return params.closest_wp;
4702 // Some forward declarations
4703 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4704 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4705 static void marker_end_move ( tool_ed_t *t );
4708 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4712 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4714 // Here always allow snapping back to the original location
4715 // this is useful when one decides not to move the thing afterall
4716 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4719 if ( event->state & GDK_CONTROL_MASK )
4721 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4723 new_coord = tp->coord;
4727 if ( event->state & GDK_SHIFT_MASK )
4729 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4731 new_coord = wp->coord;
4735 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4737 marker_moveto ( t, x, y );
4744 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4746 if ( t->holding && event->button == 1 )
4749 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4752 if ( event->state & GDK_CONTROL_MASK )
4754 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4756 new_coord = tp->coord;
4760 if ( event->state & GDK_SHIFT_MASK )
4762 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4764 new_coord = wp->coord;
4767 marker_end_move ( t );
4769 // Determine if working on a waypoint or a trackpoint
4770 if ( t->is_waypoint )
4771 vtl->current_wp->coord = new_coord;
4773 if ( vtl->current_tpl ) {
4774 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4777 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4779 // Don't really know what this is for but seems like it might be handy...
4780 /* can't join with itself! */
4781 trw_layer_cancel_last_tp ( vtl );
4786 vtl->current_wp = NULL;
4787 vtl->current_wp_name = NULL;
4788 trw_layer_cancel_current_tp ( vtl, FALSE );
4790 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4797 Returns true if a waypoint or track is found near the requested event position for this particular layer
4798 The item found is automatically selected
4799 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4801 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4803 if ( event->button != 1 )
4806 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4809 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4812 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4814 if (vtl->waypoints_visible) {
4815 WPSearchParams wp_params;
4816 wp_params.vvp = vvp;
4817 wp_params.x = event->x;
4818 wp_params.y = event->y;
4819 wp_params.closest_wp_name = NULL;
4820 wp_params.closest_wp = NULL;
4822 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4824 if ( wp_params.closest_wp ) {
4827 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4829 // Too easy to move it so must be holding shift to start immediately moving it
4830 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
4831 if ( event->state & GDK_SHIFT_MASK ||
4832 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
4833 // Put into 'move buffer'
4834 // NB vvp & vw already set in tet
4835 tet->vtl = (gpointer)vtl;
4836 tet->is_waypoint = TRUE;
4838 marker_begin_move (tet, event->x, event->y);
4841 vtl->current_wp = wp_params.closest_wp;
4842 vtl->current_wp_name = wp_params.closest_wp_name;
4844 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4850 if (vtl->tracks_visible) {
4851 TPSearchParams tp_params;
4852 tp_params.vvp = vvp;
4853 tp_params.x = event->x;
4854 tp_params.y = event->y;
4855 tp_params.closest_track_name = NULL;
4856 tp_params.closest_tp = NULL;
4858 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4860 if ( tp_params.closest_tp ) {
4862 // Always select + highlight the track
4863 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4865 tet->is_waypoint = FALSE;
4867 // Select the Trackpoint
4868 // Can move it immediately when control held or it's the previously selected tp
4869 if ( event->state & GDK_CONTROL_MASK ||
4870 vtl->current_tpl == tp_params.closest_tpl ) {
4871 // Put into 'move buffer'
4872 // NB vvp & vw already set in tet
4873 tet->vtl = (gpointer)vtl;
4874 marker_begin_move (tet, event->x, event->y);
4877 vtl->current_tpl = tp_params.closest_tpl;
4878 vtl->current_tp_track_name = tp_params.closest_track_name;
4880 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
4883 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4885 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4890 /* these aren't the droids you're looking for */
4891 vtl->current_wp = NULL;
4892 vtl->current_wp_name = NULL;
4893 trw_layer_cancel_current_tp ( vtl, FALSE );
4896 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
4901 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4903 if ( event->button != 3 )
4906 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4909 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4912 /* Post menu for the currently selected item */
4914 /* See if a track is selected */
4915 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4916 if ( track && track->visible ) {
4918 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4920 if ( vtl->track_right_click_menu )
4921 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4923 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4925 trw_layer_sublayer_add_menu_items ( vtl,
4926 vtl->track_right_click_menu,
4928 VIK_TRW_LAYER_SUBLAYER_TRACK,
4929 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4930 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4933 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4939 /* See if a waypoint is selected */
4940 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4941 if ( waypoint && waypoint->visible ) {
4942 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4944 if ( vtl->wp_right_click_menu )
4945 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4947 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4948 trw_layer_sublayer_add_menu_items ( vtl,
4949 vtl->wp_right_click_menu,
4951 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4952 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4953 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4955 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4964 /* background drawing hook, to be passed the viewport */
4965 static gboolean tool_sync_done = TRUE;
4967 static gboolean tool_sync(gpointer data)
4969 VikViewport *vvp = data;
4970 gdk_threads_enter();
4971 vik_viewport_sync(vvp);
4972 tool_sync_done = TRUE;
4973 gdk_threads_leave();
4977 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4980 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4981 gdk_gc_set_function ( t->gc, GDK_INVERT );
4982 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4983 vik_viewport_sync(t->vvp);
4988 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4990 VikViewport *vvp = t->vvp;
4991 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4992 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4996 if (tool_sync_done) {
4997 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4998 tool_sync_done = FALSE;
5002 static void marker_end_move ( tool_ed_t *t )
5004 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5005 g_object_unref ( t->gc );
5009 /*** Edit waypoint ****/
5011 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5013 tool_ed_t *t = g_new(tool_ed_t, 1);
5019 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5021 WPSearchParams params;
5022 tool_ed_t *t = data;
5023 VikViewport *vvp = t->vvp;
5025 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5032 if ( !vtl->vl.visible || !vtl->waypoints_visible )
5035 if ( vtl->current_wp && vtl->current_wp->visible )
5037 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5039 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5041 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5042 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5044 if ( event->button == 3 )
5045 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5047 marker_begin_move(t, event->x, event->y);
5054 params.x = event->x;
5055 params.y = event->y;
5056 params.closest_wp_name = NULL;
5057 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5058 params.closest_wp = NULL;
5059 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5060 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5062 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5063 marker_begin_move(t, event->x, event->y);
5064 g_critical("shouldn't be here");
5067 else if ( params.closest_wp )
5069 if ( event->button == 3 )
5070 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5072 vtl->waypoint_rightclick = FALSE;
5074 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
5076 vtl->current_wp = params.closest_wp;
5077 vtl->current_wp_name = params.closest_wp_name;
5079 /* could make it so don't update if old WP is off screen and new is null but oh well */
5080 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5084 vtl->current_wp = NULL;
5085 vtl->current_wp_name = NULL;
5086 vtl->waypoint_rightclick = FALSE;
5087 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5091 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5093 tool_ed_t *t = data;
5094 VikViewport *vvp = t->vvp;
5096 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5101 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5104 if ( event->state & GDK_CONTROL_MASK )
5106 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5108 new_coord = tp->coord;
5112 if ( event->state & GDK_SHIFT_MASK )
5114 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5115 if ( wp && wp != vtl->current_wp )
5116 new_coord = wp->coord;
5121 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5123 marker_moveto ( t, x, y );
5130 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5132 tool_ed_t *t = data;
5133 VikViewport *vvp = t->vvp;
5135 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5138 if ( t->holding && event->button == 1 )
5141 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5144 if ( event->state & GDK_CONTROL_MASK )
5146 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5148 new_coord = tp->coord;
5152 if ( event->state & GDK_SHIFT_MASK )
5154 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5155 if ( wp && wp != vtl->current_wp )
5156 new_coord = wp->coord;
5159 marker_end_move ( t );
5161 vtl->current_wp->coord = new_coord;
5162 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5165 /* PUT IN RIGHT PLACE!!! */
5166 if ( event->button == 3 && vtl->waypoint_rightclick )
5168 if ( vtl->wp_right_click_menu )
5169 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5170 if ( vtl->current_wp ) {
5171 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5172 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 );
5173 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5175 vtl->waypoint_rightclick = FALSE;
5180 /**** Begin track ***/
5181 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5186 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5188 vtl->current_track = NULL;
5189 return tool_new_track_click ( vtl, event, vvp );
5192 /*** New track ****/
5194 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5202 gint x1,y1,x2,y2,x3,y3;
5204 } new_track_move_passalong_t;
5206 /* sync and undraw, but only when we have time */
5207 static gboolean ct_sync ( gpointer passalong )
5209 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5211 vik_viewport_sync ( p->vvp );
5212 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5213 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5214 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);
5215 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5217 g_free ( (gpointer) p->str ) ;
5218 p->vtl->ct_sync_done = TRUE;
5223 static const gchar* distance_string (gdouble distance)
5227 /* draw label with distance */
5228 vik_units_distance_t dist_units = a_vik_get_units_distance ();
5229 switch (dist_units) {
5230 case VIK_UNITS_DISTANCE_MILES:
5231 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5232 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5233 } else if (distance < 1609.4) {
5234 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5236 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5240 // VIK_UNITS_DISTANCE_KILOMETRES
5241 if (distance >= 1000 && distance < 100000) {
5242 g_sprintf(str, "%3.2f km", distance/1000.0);
5243 } else if (distance < 1000) {
5244 g_sprintf(str, "%d m", (int)distance);
5246 g_sprintf(str, "%d km", (int)distance/1000);
5250 return g_strdup (str);
5254 * Actually set the message in statusbar
5256 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5258 // Only show elevation data when track has some elevation properties
5259 gchar str_gain_loss[64];
5260 str_gain_loss[0] = '\0';
5262 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5263 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5264 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5266 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5269 // Write with full gain/loss information
5270 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5271 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5276 * Figure out what information should be set in the statusbar and then write it
5278 static void update_statusbar ( VikTrwLayer *vtl )
5280 // Get elevation data
5281 gdouble elev_gain, elev_loss;
5282 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5284 /* Find out actual distance of current track */
5285 gdouble distance = vik_track_get_length (vtl->current_track);
5286 const gchar *str = distance_string (distance);
5288 statusbar_write (str, elev_gain, elev_loss, vtl);
5290 g_free ((gpointer)str);
5294 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5296 /* if we haven't sync'ed yet, we don't have time to do more. */
5297 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5298 GList *iter = vtl->current_track->trackpoints;
5299 new_track_move_passalong_t *passalong;
5302 while ( iter->next )
5304 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5305 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5306 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5308 /* Find out actual distance of current track */
5309 gdouble distance = vik_track_get_length (vtl->current_track);
5311 // Now add distance to where the pointer is //
5314 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5315 vik_coord_to_latlon ( &coord, &ll );
5316 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5318 // Get elevation data
5319 gdouble elev_gain, elev_loss;
5320 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5322 // Adjust elevation data (if available) for the current pointer position
5324 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5325 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5326 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5327 // Adjust elevation of last track point
5328 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5330 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5333 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5337 const gchar *str = distance_string (distance);
5339 /* offset from cursor a bit */
5342 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5343 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5345 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5347 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5348 passalong->vtl = vtl;
5349 passalong->vvp = vvp;
5352 passalong->x2 = event->x;
5353 passalong->y2 = event->y;
5356 passalong->str = str;
5358 // Update statusbar with full gain/loss information
5359 statusbar_write (str, elev_gain, elev_loss, vtl);
5361 /* this will sync and undraw when we have time to */
5362 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5363 vtl->ct_sync_done = FALSE;
5364 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5366 return VIK_LAYER_TOOL_ACK;
5369 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5371 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5372 vtl->current_track = NULL;
5373 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5375 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5377 if ( vtl->current_track->trackpoints )
5379 GList *last = g_list_last(vtl->current_track->trackpoints);
5380 g_free ( last->data );
5381 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5384 update_statusbar ( vtl );
5386 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5392 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5396 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5399 if ( event->button == 3 && vtl->current_track )
5402 if ( vtl->current_track->trackpoints )
5404 GList *last = g_list_last(vtl->current_track->trackpoints);
5405 g_free ( last->data );
5406 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5408 update_statusbar ( vtl );
5410 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5414 if ( event->type == GDK_2BUTTON_PRESS )
5416 /* subtract last (duplicate from double click) tp then end */
5417 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5419 GList *last = g_list_last(vtl->current_track->trackpoints);
5420 g_free ( last->data );
5421 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5422 /* undo last, then end */
5423 vtl->current_track = NULL;
5425 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5429 if ( ! vtl->current_track )
5431 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5432 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5434 vtl->current_track = vik_track_new();
5435 vtl->current_track->visible = TRUE;
5436 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5438 /* incase it was created by begin track */
5439 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5444 tp = vik_trackpoint_new();
5445 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5447 /* snap to other TP */
5448 if ( event->state & GDK_CONTROL_MASK )
5450 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5452 tp->coord = other_tp->coord;
5455 tp->newsegment = FALSE;
5456 tp->has_timestamp = FALSE;
5458 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5459 /* Auto attempt to get elevation from DEM data (if it's available) */
5460 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5462 vtl->ct_x1 = vtl->ct_x2;
5463 vtl->ct_y1 = vtl->ct_y2;
5464 vtl->ct_x2 = event->x;
5465 vtl->ct_y2 = event->y;
5467 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5471 /*** New waypoint ****/
5473 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5478 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5481 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5483 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5484 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5485 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5490 /*** Edit trackpoint ****/
5492 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5494 tool_ed_t *t = g_new(tool_ed_t, 1);
5500 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5502 tool_ed_t *t = data;
5503 VikViewport *vvp = t->vvp;
5504 TPSearchParams params;
5505 /* OUTDATED DOCUMENTATION:
5506 find 5 pixel range on each side. then put these UTM, and a pointer
5507 to the winning track name (and maybe the winning track itself), and a
5508 pointer to the winning trackpoint, inside an array or struct. pass
5509 this along, do a foreach on the tracks which will do a foreach on the
5512 params.x = event->x;
5513 params.y = event->y;
5514 params.closest_track_name = NULL;
5515 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5516 params.closest_tp = NULL;
5518 if ( event->button != 1 )
5521 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5524 if ( !vtl->vl.visible || !vtl->tracks_visible )
5527 if ( vtl->current_tpl )
5529 /* 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.) */
5530 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5531 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5533 g_assert ( current_tr );
5535 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5537 if ( current_tr->visible &&
5538 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5539 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5540 marker_begin_move ( t, event->x, event->y );
5544 vtl->last_tpl = vtl->current_tpl;
5545 vtl->last_tp_track_name = vtl->current_tp_track_name;
5548 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5550 if ( params.closest_tp )
5552 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5553 vtl->current_tpl = params.closest_tpl;
5554 vtl->current_tp_track_name = params.closest_track_name;
5555 trw_layer_tpwin_init ( vtl );
5556 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5557 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5561 /* these aren't the droids you're looking for */
5565 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5567 tool_ed_t *t = data;
5568 VikViewport *vvp = t->vvp;
5570 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5576 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5579 if ( event->state & GDK_CONTROL_MASK )
5581 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5582 if ( tp && tp != vtl->current_tpl->data )
5583 new_coord = tp->coord;
5585 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5588 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5589 marker_moveto ( t, x, y );
5597 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5599 tool_ed_t *t = data;
5600 VikViewport *vvp = t->vvp;
5602 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5604 if ( event->button != 1)
5609 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5612 if ( event->state & GDK_CONTROL_MASK )
5614 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5615 if ( tp && tp != vtl->current_tpl->data )
5616 new_coord = tp->coord;
5619 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5621 marker_end_move ( t );
5623 /* diff dist is diff from orig */
5625 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5626 /* can't join with itself! */
5627 trw_layer_cancel_last_tp ( vtl );
5629 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5636 /*** Route Finder ***/
5637 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5642 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5645 if ( !vtl ) return FALSE;
5646 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5647 if ( event->button == 3 && vtl->route_finder_current_track ) {
5649 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5651 vtl->route_finder_coord = *new_end;
5653 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5654 /* remove last ' to:...' */
5655 if ( vtl->route_finder_current_track->comment ) {
5656 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5657 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5658 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5659 last_to - vtl->route_finder_current_track->comment - 1);
5660 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5665 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5666 struct LatLon start, end;
5667 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5668 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5671 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5672 vik_coord_to_latlon ( &(tmp), &end );
5673 vtl->route_finder_coord = tmp; /* for continuations */
5675 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5676 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5677 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
5679 vtl->route_finder_check_added_track = TRUE;
5680 vtl->route_finder_started = FALSE;
5683 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5684 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5685 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5686 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5687 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5688 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5691 /* see if anything was done -- a track was added or appended to */
5692 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5695 tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5698 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5700 vtl->route_finder_current_track = tr;
5702 g_free ( vtl->route_finder_added_track_name );
5703 vtl->route_finder_added_track_name = NULL;
5704 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5705 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5706 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5707 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5709 vtl->route_finder_check_added_track = FALSE;
5710 vtl->route_finder_append = FALSE;
5712 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5714 vtl->route_finder_started = TRUE;
5715 vtl->route_finder_coord = tmp;
5716 vtl->route_finder_current_track = NULL;
5721 /*** Show picture ****/
5723 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5728 /* Params are: vvp, event, last match found or NULL */
5729 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
5731 if ( wp->image && wp->visible )
5733 gint x, y, slackx, slacky;
5734 GdkEventButton *event = (GdkEventButton *) params[1];
5736 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5737 slackx = wp->image_width / 2;
5738 slacky = wp->image_height / 2;
5739 if ( x <= event->x + slackx && x >= event->x - slackx
5740 && y <= event->y + slacky && y >= event->y - slacky )
5742 params[2] = wp->image; /* we've found a match. however continue searching
5743 * since we want to find the last match -- that
5744 * is, the match that was drawn last. */
5749 static void trw_layer_show_picture ( gpointer pass_along[6] )
5751 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5753 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5756 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5757 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
5758 g_free ( quoted_file );
5759 if ( ! g_spawn_command_line_async ( cmd, &err ) )
5761 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() );
5762 g_error_free ( err );
5765 #endif /* WINDOWS */
5768 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5770 gpointer params[3] = { vvp, event, NULL };
5771 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5773 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5776 static gpointer pass_along[6];
5777 pass_along[0] = vtl;
5778 pass_along[5] = params[2];
5779 trw_layer_show_picture ( pass_along );
5780 return TRUE; /* found a match */
5783 return FALSE; /* go through other layers, searching for a match */
5786 /***************************************************************************
5788 ***************************************************************************/
5794 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5796 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5797 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5800 /* Structure for thumbnail creating data used in the background thread */
5802 VikTrwLayer *vtl; // Layer needed for redrawing
5803 GSList *pics; // Image list
5804 } thumbnail_create_thread_data;
5806 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5808 guint total = g_slist_length(tctd->pics), done = 0;
5809 while ( tctd->pics )
5811 a_thumbnails_create ( (gchar *) tctd->pics->data );
5812 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5814 return -1; /* Abort thread */
5816 tctd->pics = tctd->pics->next;
5819 // Redraw to show the thumbnails as they are now created
5820 if ( IS_VIK_LAYER(tctd->vtl) )
5821 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
5826 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5828 while ( tctd->pics )
5830 g_free ( tctd->pics->data );
5831 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5836 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5838 if ( ! vtl->has_verified_thumbnails )
5840 GSList *pics = NULL;
5841 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5844 gint len = g_slist_length ( pics );
5845 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5846 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5849 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5851 (vik_thr_func) create_thumbnails_thread,
5853 (vik_thr_free_func) thumbnail_create_thread_free,
5861 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5863 return vtl->coord_mode;
5868 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5870 vik_coord_convert ( &(wp->coord), *dest_mode );
5873 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5875 vik_track_convert ( tr, *dest_mode );
5878 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5880 if ( vtl->coord_mode != dest_mode )
5882 vtl->coord_mode = dest_mode;
5883 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5884 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5888 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5890 return g_hash_table_lookup ( vtl->waypoints, name );
5893 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5895 return g_hash_table_lookup ( vtl->tracks, name );
5898 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5900 vtl->menu_selection = selection;
5903 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5905 return (vtl->menu_selection);
5908 /* ----------- Downloading maps along tracks --------------- */
5910 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5912 /* TODO: calculating based on current size of viewport */
5913 const gdouble w_at_zoom_0_125 = 0.0013;
5914 const gdouble h_at_zoom_0_125 = 0.0011;
5915 gdouble zoom_factor = zoom_level/0.125;
5917 wh->lat = h_at_zoom_0_125 * zoom_factor;
5918 wh->lon = w_at_zoom_0_125 * zoom_factor;
5920 return 0; /* all OK */
5923 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5925 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5926 (dist->lat >= ABS(to->north_south - from->north_south)))
5929 VikCoord *coord = g_malloc(sizeof(VikCoord));
5930 coord->mode = VIK_COORD_LATLON;
5932 if (ABS(gradient) < 1) {
5933 if (from->east_west > to->east_west)
5934 coord->east_west = from->east_west - dist->lon;
5936 coord->east_west = from->east_west + dist->lon;
5937 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5939 if (from->north_south > to->north_south)
5940 coord->north_south = from->north_south - dist->lat;
5942 coord->north_south = from->north_south + dist->lat;
5943 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5949 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5951 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5952 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5954 VikCoord *next = from;
5956 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5958 list = g_list_prepend(list, next);
5964 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5966 typedef struct _Rect {
5971 #define GLRECT(iter) ((Rect *)((iter)->data))
5974 GList *rects_to_download = NULL;
5977 if (get_download_area_width(vvp, zoom_level, &wh))
5980 GList *iter = tr->trackpoints;
5984 gboolean new_map = TRUE;
5985 VikCoord *cur_coord, tl, br;
5988 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5990 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5991 rect = g_malloc(sizeof(Rect));
5994 rect->center = *cur_coord;
5995 rects_to_download = g_list_prepend(rects_to_download, rect);
6000 gboolean found = FALSE;
6001 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6002 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
6013 GList *fillins = NULL;
6014 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
6015 /* seems that ATM the function get_next_coord works only for LATLON */
6016 if ( cur_coord->mode == VIK_COORD_LATLON ) {
6017 /* fill-ins for far apart points */
6018 GList *cur_rect, *next_rect;
6019 for (cur_rect = rects_to_download;
6020 (next_rect = cur_rect->next) != NULL;
6021 cur_rect = cur_rect->next) {
6022 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
6023 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
6024 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
6028 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
6031 GList *iter = fillins;
6033 cur_coord = (VikCoord *)(iter->data);
6034 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6035 rect = g_malloc(sizeof(Rect));
6038 rect->center = *cur_coord;
6039 rects_to_download = g_list_prepend(rects_to_download, rect);
6044 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6045 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
6049 for (iter = fillins; iter; iter = iter->next)
6051 g_list_free(fillins);
6053 if (rects_to_download) {
6054 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
6055 g_free(rect_iter->data);
6056 g_list_free(rects_to_download);
6060 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6063 gint selected_map, default_map;
6064 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6065 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6066 gint selected_zoom, default_zoom;
6070 VikTrwLayer *vtl = pass_along[0];
6071 VikLayersPanel *vlp = pass_along[1];
6072 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6073 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6075 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6076 int num_maps = g_list_length(vmls);
6079 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6083 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6084 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6086 gchar **np = map_names;
6087 VikMapsLayer **lp = map_layers;
6088 for (i = 0; i < num_maps; i++) {
6089 gboolean dup = FALSE;
6090 vml = (VikMapsLayer *)(vmls->data);
6091 for (j = 0; j < i; j++) { /* no duplicate allowed */
6092 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6099 *np++ = vik_maps_layer_get_map_label(vml);
6105 num_maps = lp - map_layers;
6107 for (default_map = 0; default_map < num_maps; default_map++) {
6108 /* TODO: check for parent layer's visibility */
6109 if (VIK_LAYER(map_layers[default_map])->visible)
6112 default_map = (default_map == num_maps) ? 0 : default_map;
6114 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6115 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6116 if (cur_zoom == zoom_vals[default_zoom])
6119 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6121 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6124 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6127 for (i = 0; i < num_maps; i++)
6128 g_free(map_names[i]);
6136 /**** lowest waypoint number calculation ***/
6137 static gint highest_wp_number_name_to_number(const gchar *name) {
6138 if ( strlen(name) == 3 ) {
6140 if ( n < 100 && name[0] != '0' )
6142 if ( n < 10 && name[0] != '0' )
6150 static void highest_wp_number_reset(VikTrwLayer *vtl)
6152 vtl->highest_wp_number = -1;
6155 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6157 /* if is bigger that top, add it */
6158 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6159 if ( new_wp_num > vtl->highest_wp_number )
6160 vtl->highest_wp_number = new_wp_num;
6163 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6165 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6166 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6167 if ( vtl->highest_wp_number == old_wp_num ) {
6169 vtl->highest_wp_number --;
6171 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6172 /* search down until we find something that *does* exist */
6174 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
6175 vtl->highest_wp_number --;
6176 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6181 /* get lowest unused number */
6182 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6185 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6187 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6188 return g_strdup(buf);