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 PangoFontDescription *pfd;
1560 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1561 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1562 pango_layout_set_font_description (rv->wplabellayout, pfd);
1563 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1564 pango_font_description_free (pfd);
1566 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1568 trw_layer_new_track_gcs ( rv, vp );
1570 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1571 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1572 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1573 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1575 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1577 rv->has_verified_thumbnails = FALSE;
1578 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1580 rv->wp_draw_symbols = TRUE;
1582 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1584 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1589 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1591 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1593 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1594 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 );
1596 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 );
1599 *new_iter = *((GtkTreeIter *) pass_along[1]);
1600 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1602 if ( ! track->visible )
1603 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1606 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1608 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1609 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1610 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 );
1612 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 );
1615 *new_iter = *((GtkTreeIter *) pass_along[1]);
1616 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1618 if ( ! wp->visible )
1619 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1623 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1626 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1628 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1629 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1631 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1633 if ( ! vtl->tracks_visible )
1634 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1636 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1638 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1639 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1641 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1644 if ( ! vtl->waypoints_visible )
1645 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1647 pass_along[0] = &(vtl->waypoints_iter);
1648 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1650 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1654 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1658 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1659 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1660 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1662 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1664 return (t->visible ^= 1);
1668 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1670 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1672 return (t->visible ^= 1);
1681 * Return a property about tracks for this layer
1683 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1685 return vtl->line_thickness;
1688 // Structure to hold multiple track information for a layer
1697 * Build up layer multiple track information via updating the tooltip_tracks structure
1699 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1701 tt->length = tt->length + vik_track_get_length (tr);
1703 // Ensure times are available
1704 if ( tr->trackpoints &&
1705 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1706 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1709 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1710 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1712 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1713 // Hence initialize to the first 'proper' value
1714 if ( tt->start_time == 0 )
1715 tt->start_time = t1;
1716 if ( tt->end_time == 0 )
1719 // Update find the earliest / last times
1720 if ( t1 < tt->start_time )
1721 tt->start_time = t1;
1722 if ( t2 > tt->end_time )
1725 // Keep track of total time
1726 // there maybe gaps within a track (eg segments)
1727 // but this should be generally good enough for a simple indicator
1728 tt->duration = tt->duration + (int)(t2-t1);
1733 * Generate tooltip text for the layer.
1734 * This is relatively complicated as it considers information for
1735 * no tracks, a single track or multiple tracks
1736 * (which may or may not have timing information)
1738 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1749 static gchar tmp_buf[128];
1752 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1754 // Safety check - I think these should always be valid
1755 if ( vtl->tracks && vtl->waypoints ) {
1756 tooltip_tracks tt = { 0.0, 0, 0 };
1757 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1759 GDate* gdate_start = g_date_new ();
1760 g_date_set_time_t (gdate_start, tt.start_time);
1762 GDate* gdate_end = g_date_new ();
1763 g_date_set_time_t (gdate_end, tt.end_time);
1765 if ( g_date_compare (gdate_start, gdate_end) ) {
1766 // Dates differ so print range on separate line
1767 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1768 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1769 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1772 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1773 if ( tt.start_time != 0 )
1774 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1778 if ( tt.length > 0.0 ) {
1779 gdouble len_in_units;
1781 // Setup info dependent on distance units
1782 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1783 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1784 len_in_units = VIK_METERS_TO_MILES(tt.length);
1787 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1788 len_in_units = tt.length/1000.0;
1791 // Timing information if available
1793 if ( tt.duration > 0 ) {
1794 g_snprintf (tbuf1, sizeof(tbuf1),
1795 _(" in %d:%02d hrs:mins"),
1796 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1798 g_snprintf (tbuf2, sizeof(tbuf2),
1799 _("\n%sTotal Length %.1f %s%s"),
1800 tbuf3, len_in_units, tbuf4, tbuf1);
1803 // Put together all the elements to form compact tooltip text
1804 g_snprintf (tmp_buf, sizeof(tmp_buf),
1805 _("Tracks: %d - Waypoints: %d%s"),
1806 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1808 g_date_free (gdate_start);
1809 g_date_free (gdate_end);
1816 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1820 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1821 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1822 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1824 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1826 // Could be a better way of handling strings - but this works...
1827 gchar time_buf1[20];
1828 gchar time_buf2[20];
1829 time_buf1[0] = '\0';
1830 time_buf2[0] = '\0';
1831 static gchar tmp_buf[100];
1832 // Compact info: Short date eg (11/20/99), duration and length
1833 // Hopefully these are the things that are most useful and so promoted into the tooltip
1834 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1835 // %x The preferred date representation for the current locale without the time.
1836 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1837 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1838 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1840 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1843 // Get length and consider the appropriate distance units
1844 gdouble tr_len = vik_track_get_length(tr);
1845 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1846 switch (dist_units) {
1847 case VIK_UNITS_DISTANCE_KILOMETRES:
1848 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1850 case VIK_UNITS_DISTANCE_MILES:
1851 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1860 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1862 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1863 // NB It's OK to return NULL
1873 * Function to show basic track point information on the statusbar
1875 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1878 switch (a_vik_get_units_height ()) {
1879 case VIK_UNITS_HEIGHT_FEET:
1880 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1883 //VIK_UNITS_HEIGHT_METRES:
1884 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1889 if ( trkpt->has_timestamp ) {
1890 // Compact date time format
1891 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1895 // Position is put later on, as this bit may not be seen if the display is not big enough,
1896 // one can easily use the current pointer position to see this if needed
1897 gchar *lat = NULL, *lon = NULL;
1898 static struct LatLon ll;
1899 vik_coord_to_latlon (&(trkpt->coord), &ll);
1900 a_coords_latlon_to_string ( &ll, &lat, &lon );
1903 // Again is put later on, as this bit may not be seen if the display is not big enough
1904 // trackname can be seen from the treeview (when enabled)
1905 // Also name could be very long to not leave room for anything else
1908 if ( vtl->current_tp_track_name ) {
1909 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track_name );
1912 // Combine parts to make overall message
1913 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
1914 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1921 * Function to show basic waypoint information on the statusbar
1923 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
1926 switch (a_vik_get_units_height ()) {
1927 case VIK_UNITS_HEIGHT_FEET:
1928 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
1931 //VIK_UNITS_HEIGHT_METRES:
1932 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
1936 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
1937 // one can easily use the current pointer position to see this if needed
1938 gchar *lat = NULL, *lon = NULL;
1939 static struct LatLon ll;
1940 vik_coord_to_latlon (&(wpt->coord), &ll);
1941 a_coords_latlon_to_string ( &ll, &lat, &lon );
1943 // Combine parts to make overall message
1946 // Add comment if available
1947 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
1949 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
1950 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1957 * General layer selection function, find out which bit is selected and take appropriate action
1959 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1962 l->current_wp = NULL;
1963 l->current_wp_name = NULL;
1964 trw_layer_cancel_current_tp ( l, FALSE );
1967 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
1971 case VIK_TREEVIEW_TYPE_LAYER:
1973 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1974 /* Mark for redraw */
1979 case VIK_TREEVIEW_TYPE_SUBLAYER:
1983 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1985 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1986 /* Mark for redraw */
1990 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1992 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
1993 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
1994 /* Mark for redraw */
1998 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2000 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2001 /* Mark for redraw */
2005 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2007 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2008 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
2009 // Show some waypoint info
2010 set_statusbar_msg_info_wpt ( l, wpt );
2011 /* Mark for redraw */
2017 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2026 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2031 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2036 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2038 return l->waypoints;
2041 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2043 static VikCoord fixme;
2044 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2045 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2046 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2047 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2048 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2049 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2050 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2051 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2052 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2055 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
2058 static VikCoord fixme;
2062 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2063 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2064 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2065 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2066 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2067 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2068 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2069 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2070 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2075 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2077 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2078 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2080 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2081 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2082 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2083 maxmin[0].lat = wpt_maxmin[0].lat;
2086 maxmin[0].lat = trk_maxmin[0].lat;
2088 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2089 maxmin[0].lon = wpt_maxmin[0].lon;
2092 maxmin[0].lon = trk_maxmin[0].lon;
2094 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2095 maxmin[1].lat = wpt_maxmin[1].lat;
2098 maxmin[1].lat = trk_maxmin[1].lat;
2100 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2101 maxmin[1].lon = wpt_maxmin[1].lon;
2104 maxmin[1].lon = trk_maxmin[1].lon;
2108 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2110 /* 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... */
2111 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2112 trw_layer_find_maxmin (vtl, maxmin);
2113 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2117 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2118 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2123 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2126 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2127 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2129 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2132 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2134 /* First set the center [in case previously viewing from elsewhere] */
2135 /* Then loop through zoom levels until provided positions are in view */
2136 /* This method is not particularly fast - but should work well enough */
2137 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2139 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2140 vik_viewport_set_center_coord ( vvp, &coord );
2142 /* Convert into definite 'smallest' and 'largest' positions */
2143 struct LatLon minmin;
2144 if ( maxmin[0].lat < maxmin[1].lat )
2145 minmin.lat = maxmin[0].lat;
2147 minmin.lat = maxmin[1].lat;
2149 struct LatLon maxmax;
2150 if ( maxmin[0].lon > maxmin[1].lon )
2151 maxmax.lon = maxmin[0].lon;
2153 maxmax.lon = maxmin[1].lon;
2155 /* Never zoom in too far - generally not that useful, as too close ! */
2156 /* Always recalculate the 'best' zoom level */
2158 vik_viewport_set_zoom ( vvp, zoom );
2160 gdouble min_lat, max_lat, min_lon, max_lon;
2161 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2162 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2163 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2164 /* NB I think the logic used in this test to determine if the bounds is within view
2165 fails if track goes across 180 degrees longitude.
2166 Hopefully that situation is not too common...
2167 Mind you viking doesn't really do edge locations to well anyway */
2168 if ( min_lat < minmin.lat &&
2169 max_lat > minmin.lat &&
2170 min_lon < maxmax.lon &&
2171 max_lon > maxmax.lon )
2172 /* Found within zoom level */
2177 vik_viewport_set_zoom ( vvp, zoom );
2181 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2183 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2184 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2185 trw_layer_find_maxmin (vtl, maxmin);
2186 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2189 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2194 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2196 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])) ) ) {
2197 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2200 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2203 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2205 GtkWidget *file_selector;
2207 gboolean failed = FALSE;
2208 file_selector = gtk_file_chooser_dialog_new (title,
2210 GTK_FILE_CHOOSER_ACTION_SAVE,
2211 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2212 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2214 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2216 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2218 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2219 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2221 gtk_widget_hide ( file_selector );
2222 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2227 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2229 gtk_widget_hide ( file_selector );
2230 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2235 gtk_widget_destroy ( file_selector );
2237 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2240 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2242 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2245 static void trw_layer_export_gpsmapper ( 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_GPSMAPPER );
2250 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2252 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2253 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2254 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2255 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2257 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2259 g_free ( auto_save_name );
2262 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2264 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2265 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2266 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2267 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2269 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2271 g_free ( auto_save_name );
2274 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2276 gpointer layer_and_vlp[2];
2277 layer_and_vlp[0] = pass_along[0];
2278 layer_and_vlp[1] = pass_along[1];
2280 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2281 gchar *auto_save_name = g_strdup ( pass_along[3] );
2282 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2283 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2285 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2287 g_free ( auto_save_name );
2290 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2292 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2293 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2294 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2295 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2297 GTK_RESPONSE_REJECT,
2299 GTK_RESPONSE_ACCEPT,
2302 GtkWidget *label, *entry;
2303 label = gtk_label_new(_("Waypoint Name:"));
2304 entry = gtk_entry_new();
2306 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2307 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2308 gtk_widget_show_all ( label );
2309 gtk_widget_show_all ( entry );
2311 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2313 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2316 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2319 for ( i = strlen(upname)-1; i >= 0; i-- )
2320 upname[i] = toupper(upname[i]);
2322 wp = g_hash_table_lookup ( wps, upname );
2325 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2328 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2329 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2330 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 );
2337 gtk_widget_destroy ( dia );
2340 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2342 gchar *default_name = highest_wp_number_get(vtl);
2343 VikWaypoint *wp = vik_waypoint_new();
2344 gchar *returned_name;
2346 wp->coord = *def_coord;
2348 // Attempt to auto set height if DEM data is available
2349 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2350 if ( elev != VIK_DEM_INVALID_ELEVATION )
2351 wp->altitude = (gdouble)elev;
2353 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2356 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2357 g_free (default_name);
2360 g_free (default_name);
2361 vik_waypoint_free(wp);
2365 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2368 struct LatLon one_ll, two_ll;
2369 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2371 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2372 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2373 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2374 VikViewport *vvp = vik_window_viewport(vw);
2375 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2376 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2377 vik_coord_to_latlon(&one, &one_ll);
2378 vik_coord_to_latlon(&two, &two_ll);
2379 if (one_ll.lat > two_ll.lat) {
2380 maxmin[0].lat = one_ll.lat;
2381 maxmin[1].lat = two_ll.lat;
2384 maxmin[0].lat = two_ll.lat;
2385 maxmin[1].lat = one_ll.lat;
2387 if (one_ll.lon > two_ll.lon) {
2388 maxmin[0].lon = one_ll.lon;
2389 maxmin[1].lon = two_ll.lon;
2392 maxmin[0].lon = two_ll.lon;
2393 maxmin[1].lon = one_ll.lon;
2395 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2398 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2400 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2401 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2402 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2404 trw_layer_find_maxmin (vtl, maxmin);
2405 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2408 #ifdef VIK_CONFIG_GEOTAG
2409 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2411 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2413 // Update directly - not changing the mtime
2414 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2417 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2419 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2422 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2426 * Use code in separate file for this feature as reasonably complex
2428 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2430 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2431 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2432 // Unset so can be reverified later if necessary
2433 vtl->has_verified_thumbnails = FALSE;
2435 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2441 static void trw_layer_geotagging ( gpointer lav[2] )
2443 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2444 // Unset so can be reverified later if necessary
2445 vtl->has_verified_thumbnails = FALSE;
2447 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2454 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2457 * Acquire into this TRW Layer straight from GPS Device
2459 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2461 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2462 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2463 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2464 VikViewport *vvp = vik_window_viewport(vw);
2466 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2467 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2471 * Acquire into this TRW Layer from Google Directions
2473 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2475 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2476 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2477 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2478 VikViewport *vvp = vik_window_viewport(vw);
2480 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2483 #ifdef VIK_CONFIG_OPENSTREETMAP
2485 * Acquire into this TRW Layer from OSM
2487 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2489 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2490 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2491 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2492 VikViewport *vvp = vik_window_viewport(vw);
2494 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2498 #ifdef VIK_CONFIG_GEOCACHES
2500 * Acquire into this TRW Layer from Geocaching.com
2502 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2504 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2505 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2506 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2507 VikViewport *vvp = vik_window_viewport(vw);
2509 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2513 #ifdef VIK_CONFIG_GEOTAG
2515 * Acquire into this TRW Layer from images
2517 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2519 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2520 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2521 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2522 VikViewport *vvp = vik_window_viewport(vw);
2524 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2525 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2527 // Reverify thumbnails as they may have changed
2528 vtl->has_verified_thumbnails = FALSE;
2529 trw_layer_verify_thumbnails ( vtl, NULL );
2533 static void trw_layer_new_wp ( gpointer lav[2] )
2535 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2536 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2537 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2538 instead return true if you want to update. */
2539 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 )
2540 vik_layers_panel_emit_update ( vlp );
2543 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2545 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2546 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2548 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2549 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2550 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2551 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2552 vik_layers_panel_emit_update ( vlp );
2556 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2558 /* NB do not care if wp is visible or not */
2559 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2562 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2564 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2565 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2567 /* Only 1 waypoint - jump straight to it */
2568 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2569 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2570 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2572 /* If at least 2 waypoints - find center and then zoom to fit */
2573 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2575 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2576 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2577 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2580 vik_layers_panel_emit_update ( vlp );
2583 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2585 static gpointer pass_along[2];
2587 GtkWidget *export_submenu;
2588 GtkWidget *wikipedia_submenu;
2589 pass_along[0] = vtl;
2590 pass_along[1] = vlp;
2592 item = gtk_menu_item_new();
2593 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2594 gtk_widget_show ( item );
2596 /* Now with icons */
2597 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2598 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2600 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2601 gtk_widget_show ( item );
2603 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2604 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2605 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2606 gtk_widget_show ( item );
2608 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2609 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2610 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2611 gtk_widget_show ( item );
2613 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2614 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2615 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2616 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2617 gtk_widget_show ( item );
2619 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2620 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2621 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2622 gtk_widget_show ( item );
2624 export_submenu = gtk_menu_new ();
2625 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2626 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2627 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2628 gtk_widget_show ( item );
2629 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2631 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2632 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2633 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2634 gtk_widget_show ( item );
2636 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2637 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2638 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2639 gtk_widget_show ( item );
2641 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2642 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2643 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2644 gtk_widget_show ( item );
2646 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2647 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2648 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2649 gtk_widget_show ( item );
2651 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2652 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2653 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2654 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2655 gtk_widget_show ( item );
2657 #ifdef VIK_CONFIG_GEONAMES
2658 wikipedia_submenu = gtk_menu_new();
2659 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2660 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2661 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2662 gtk_widget_show(item);
2663 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2665 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2666 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2667 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2668 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2669 gtk_widget_show ( item );
2671 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2672 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2673 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2674 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2675 gtk_widget_show ( item );
2678 #ifdef VIK_CONFIG_GEOTAG
2679 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
2680 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
2681 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2682 gtk_widget_show ( item );
2685 GtkWidget *acquire_submenu = gtk_menu_new ();
2686 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2687 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2688 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2689 gtk_widget_show ( item );
2690 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2692 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2693 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2694 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2695 gtk_widget_show ( item );
2697 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2698 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2699 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2700 gtk_widget_show ( item );
2702 #ifdef VIK_CONFIG_OPENSTREETMAP
2703 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2705 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2706 gtk_widget_show ( item );
2709 #ifdef VIK_CONFIG_GEOCACHES
2710 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2711 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2712 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2713 gtk_widget_show ( item );
2716 #ifdef VIK_CONFIG_GEOTAG
2717 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
2718 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
2719 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2720 gtk_widget_show ( item );
2723 #ifdef VIK_CONFIG_OPENSTREETMAP
2724 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2725 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2726 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2727 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2728 gtk_widget_show ( item );
2731 GtkWidget *delete_submenu = gtk_menu_new ();
2732 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2733 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2734 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2735 gtk_widget_show ( item );
2736 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2738 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2739 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2741 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2742 gtk_widget_show ( item );
2744 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2745 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2746 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2747 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2748 gtk_widget_show ( item );
2750 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2751 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2752 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2753 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2754 gtk_widget_show ( item );
2756 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2757 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2758 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2759 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2760 gtk_widget_show ( item );
2762 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2763 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2765 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2766 gtk_widget_show ( item );
2769 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2770 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2772 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2773 gtk_widget_show ( item );
2777 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2779 if ( VIK_LAYER(vtl)->realized )
2781 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2783 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2786 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2787 // Visibility column always needed for waypoints
2788 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2789 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2791 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2793 // Actual setting of visibility dependent on the waypoint
2794 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2795 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2799 highest_wp_number_add_wp(vtl, name);
2800 g_hash_table_insert ( vtl->waypoints, name, wp );
2804 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2806 if ( VIK_LAYER(vtl)->realized )
2808 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2810 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2813 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2814 // Visibility column always needed for tracks
2815 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2816 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2818 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2820 // Actual setting of visibility dependent on the track
2821 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2822 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2826 g_hash_table_insert ( vtl->tracks, name, t );
2830 /* to be called whenever a track has been deleted or may have been changed. */
2831 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2833 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2834 trw_layer_cancel_current_tp ( vtl, FALSE );
2835 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2836 trw_layer_cancel_last_tp ( vtl );
2839 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2842 gchar *newname = g_strdup(name);
2843 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2844 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2845 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2847 newname = new_newname;
2853 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2855 vik_trw_layer_add_waypoint ( vtl,
2856 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2859 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2861 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
2862 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2863 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
2864 vik_track_free ( tr );
2865 vtl->route_finder_append = FALSE; /* this means we have added it */
2867 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2868 vik_trw_layer_add_track ( vtl, new_name, tr );
2870 if ( vtl->route_finder_check_added_track ) {
2871 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2872 if ( vtl->route_finder_added_track_name ) /* for google routes */
2873 g_free ( vtl->route_finder_added_track_name );
2874 vtl->route_finder_added_track_name = g_strdup(new_name);
2879 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2881 *l = g_list_append(*l, (gpointer)name);
2884 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2886 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2887 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2889 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2890 vik_trw_layer_delete_track(vtl_src, name);
2891 vik_trw_layer_add_track(vtl_dest, newname, t);
2893 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2895 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2896 vik_trw_layer_delete_waypoint(vtl_src, name);
2897 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2901 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2903 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2904 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2906 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2907 GList *items = NULL;
2910 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2911 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2913 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2914 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2919 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2920 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2922 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2929 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2930 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2934 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2936 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2937 gboolean was_visible = FALSE;
2941 was_visible = t->visible;
2942 if ( t == vtl->current_track ) {
2943 vtl->current_track = NULL;
2945 if ( t == vtl->route_finder_current_track )
2946 vtl->route_finder_current_track = NULL;
2948 /* could be current_tp, so we have to check */
2949 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2951 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2952 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2953 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2955 /* do this last because trk_name may be pointing to actual orig key */
2956 g_hash_table_remove ( vtl->tracks, trk_name );
2961 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2963 gboolean was_visible = FALSE;
2966 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2970 if ( wp == vtl->current_wp ) {
2971 vtl->current_wp = NULL;
2972 vtl->current_wp_name = NULL;
2973 vtl->moving_wp = FALSE;
2976 was_visible = wp->visible;
2977 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2978 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2979 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2981 highest_wp_number_remove_wp(vtl, wp_name);
2982 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2988 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2990 vik_treeview_item_delete (vt, it );
2993 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2996 vtl->current_track = NULL;
2997 vtl->route_finder_current_track = NULL;
2998 if (vtl->current_tp_track_name)
2999 trw_layer_cancel_current_tp(vtl, FALSE);
3000 if (vtl->last_tp_track_name)
3001 trw_layer_cancel_last_tp ( vtl );
3003 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3004 g_hash_table_remove_all(vtl->tracks_iters);
3005 g_hash_table_remove_all(vtl->tracks);
3007 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3010 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3012 vtl->current_wp = NULL;
3013 vtl->current_wp_name = NULL;
3014 vtl->moving_wp = FALSE;
3016 highest_wp_number_reset(vtl);
3018 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3019 g_hash_table_remove_all(vtl->waypoints_iters);
3020 g_hash_table_remove_all(vtl->waypoints);
3022 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3025 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3027 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3028 // Get confirmation from the user
3029 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3030 _("Are you sure you want to delete all tracks in %s?"),
3031 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3032 vik_trw_layer_delete_all_tracks (vtl);
3035 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3037 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3038 // Get confirmation from the user
3039 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3040 _("Are you sure you want to delete all waypoints in %s?"),
3041 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3042 vik_trw_layer_delete_all_waypoints (vtl);
3045 static void trw_layer_delete_item ( gpointer pass_along[6] )
3047 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3048 gboolean was_visible = FALSE;
3049 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3051 if ( GPOINTER_TO_INT ( pass_along[4]) )
3052 // Get confirmation from the user
3053 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3054 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3055 _("Are you sure you want to delete the waypoint \"%s\""),
3058 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
3062 if ( GPOINTER_TO_INT ( pass_along[4]) )
3063 // Get confirmation from the user
3064 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3065 _("Are you sure you want to delete the track \"%s\""),
3068 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
3071 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3075 static void trw_layer_properties_item ( gpointer pass_along[6] )
3077 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3078 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3080 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3083 gboolean updated = FALSE;
3084 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
3086 if ( updated && VIK_LAYER(vtl)->visible )
3087 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3092 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3095 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3097 pass_along[1], /* vlp */
3098 pass_along[3], /* track name */
3099 pass_along[5] ); /* vvp */
3105 Parameter 1 -> VikLayersPanel
3106 Parameter 2 -> VikLayer
3107 Parameter 3 -> VikViewport
3109 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3112 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3113 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3116 /* since vlp not set, vl & vvp should be valid instead! */
3118 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3119 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3124 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3126 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3127 if ( trps && trps->data )
3128 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3131 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3133 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
3134 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3135 if ( trps && *trps )
3137 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3139 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3140 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3141 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3142 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3143 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3147 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3149 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3150 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3152 vtl->current_track = track;
3153 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3155 if ( track->trackpoints )
3156 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3160 * extend a track using route finder
3162 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3164 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3165 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3166 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3168 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3169 vtl->route_finder_coord = last_coord;
3170 vtl->route_finder_current_track = track;
3171 vtl->route_finder_started = TRUE;
3173 if ( track->trackpoints )
3174 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3178 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3180 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3181 /* Also warn if overwrite old elevation data */
3182 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3184 vik_track_apply_dem_data ( track );
3187 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3189 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3192 trps = g_list_last(trps);
3193 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3196 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3198 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3201 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3204 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3206 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3209 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3212 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3214 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3217 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3221 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3223 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3225 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3226 if ( trps && *trps )
3228 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3229 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3230 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3231 if ( pass_along[1] )
3232 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3234 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3238 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3240 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3241 trw_layer_tpwin_init ( vtl );
3244 /*************************************
3245 * merge/split by time routines
3246 *************************************/
3248 /* called for each key in track hash table.
3249 * If the current track has the same time stamp type, add it to the result,
3250 * except the one pointed by "exclude".
3251 * set exclude to NULL if there is no exclude to check.
3252 * Note that the result is in reverse (for performance reasons).
3257 gboolean with_timestamps;
3259 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3261 twt_udata *user_data = udata;
3262 VikTrackpoint *p1, *p2;
3264 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3268 if (VIK_TRACK(value)->trackpoints) {
3269 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3270 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3272 if ( user_data->with_timestamps ) {
3273 if (!p1->has_timestamp || !p2->has_timestamp) {
3278 // Don't add tracks with timestamps when getting non timestamp tracks
3279 if (p1->has_timestamp || p2->has_timestamp) {
3285 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3288 /* called for each key in track hash table. if original track user_data[1] is close enough
3289 * to the passed one, add it to list in user_data[0]
3291 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3294 VikTrackpoint *p1, *p2;
3296 GList **nearby_tracks = ((gpointer *)user_data)[0];
3297 GList *orig_track = ((gpointer *)user_data)[1];
3298 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3301 * detect reasons for not merging, and return
3302 * if no reason is found not to merge, then do it.
3305 if (VIK_TRACK(value)->trackpoints == orig_track) {
3309 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3310 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3312 if (VIK_TRACK(value)->trackpoints) {
3313 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3314 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3316 if (!p1->has_timestamp || !p2->has_timestamp) {
3317 g_print("no timestamp\n");
3321 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3322 if (! (abs(t1 - p2->timestamp) < thr*60 ||
3324 abs(p1->timestamp - t2) < thr*60)
3331 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3334 /* comparison function used to sort tracks; a and b are hash table keys */
3335 /* Not actively used - can be restored if needed
3336 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3338 GHashTable *tracks = user_data;
3341 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3342 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3344 if (t1 < t2) return -1;
3345 if (t1 > t2) return 1;
3350 /* comparison function used to sort trackpoints */
3351 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3353 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3355 if (t1 < t2) return -1;
3356 if (t1 > t2) return 1;
3360 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3362 * comparison function which can be used to sort tracks or waypoints by name
3364 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3366 const gchar* namea = (const gchar*) a;
3367 const gchar* nameb = (const gchar*) b;
3368 if ( namea == NULL || nameb == NULL)
3371 // Same sort method as used in the vik_treeview_*_alphabetize functions
3372 return strcmp ( namea, nameb );
3377 * Attempt to merge selected track with other tracks specified by the user
3378 * Tracks to merge with must be of the same 'type' as the selected track -
3379 * either all with timestamps, or all without timestamps
3381 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3383 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3384 gchar *orig_track_name = pass_along[3];
3385 GList *other_tracks = NULL;
3386 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3388 if ( !track->trackpoints )
3392 udata.result = &other_tracks;
3393 udata.exclude = track->trackpoints;
3394 // Allow merging with 'similar' time type time tracks
3395 // i.e. either those times, or those without
3396 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3398 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3399 other_tracks = g_list_reverse(other_tracks);
3401 if ( !other_tracks ) {
3402 if ( udata.with_timestamps )
3403 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3405 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3409 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3410 // Sort alphabetically for user presentation
3411 other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3414 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3416 _("Merge with..."), _("Select track to merge with"));
3417 g_list_free(other_tracks);
3422 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3423 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3425 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3426 merge_track->trackpoints = NULL;
3427 vik_trw_layer_delete_track(vtl, l->data);
3428 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3431 /* TODO: free data before free merge_list */
3432 for (l = merge_list; l != NULL; l = g_list_next(l))
3434 g_list_free(merge_list);
3435 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3439 /* merge by time routine */
3440 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3442 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3443 gchar *orig_track_name = strdup(pass_along[3]);
3446 GList *nearby_tracks;
3449 static guint thr = 1;
3450 guint track_count = 0;
3452 GList *tracks_with_timestamp = NULL;
3453 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3454 if (track->trackpoints &&
3455 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3456 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3457 free(orig_track_name);
3462 udata.result = &tracks_with_timestamp;
3463 udata.exclude = track->trackpoints;
3464 udata.with_timestamps = TRUE;
3465 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3466 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3468 if (!tracks_with_timestamp) {
3469 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3470 free(orig_track_name);
3473 g_list_free(tracks_with_timestamp);
3475 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3476 _("Merge Threshold..."),
3477 _("Merge when time between tracks less than:"),
3479 free(orig_track_name);
3483 /* merge tracks until we can't */
3484 nearby_tracks = NULL;
3488 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3489 trps = track->trackpoints;
3494 if (nearby_tracks) {
3495 g_list_free(nearby_tracks);
3496 nearby_tracks = NULL;
3499 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3500 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3502 /* g_print("Original track times: %d and %d\n", t1, t2); */
3503 params[0] = &nearby_tracks;
3505 params[2] = GUINT_TO_POINTER (thr);
3507 /* get a list of adjacent-in-time tracks */
3508 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3510 /* add original track */
3511 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3515 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3516 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3517 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3518 GList *l = nearby_tracks;
3519 VikTrack *tr = vik_track_new();
3520 tr->visible = track->visible;
3525 t1 = get_first_trackpoint(l)->timestamp;
3526 t2 = get_last_trackpoint(l)->timestamp;
3527 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3531 /* remove trackpoints from merged track, delete track */
3532 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3533 get_track(l)->trackpoints = NULL;
3534 vik_trw_layer_delete_track(vtl, l->data);
3539 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3540 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3542 #undef get_first_trackpoint
3543 #undef get_last_trackpoint
3546 } while (track_count > 1);
3547 g_list_free(nearby_tracks);
3548 free(orig_track_name);
3549 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3552 /* split by time routine */
3553 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3555 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3556 GList *trps = track->trackpoints;
3558 GList *newlists = NULL;
3559 GList *newtps = NULL;
3561 static guint thr = 1;
3568 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3569 _("Split Threshold..."),
3570 _("Split when time between trackpoints exceeds:"),
3575 /* iterate through trackpoints, and copy them into new lists without touching original list */
3576 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3580 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3582 g_print("panic: ts < prev_ts: this should never happen!\n");
3585 if (ts - prev_ts > thr*60) {
3586 /* flush accumulated trackpoints into new list */
3587 newlists = g_list_append(newlists, g_list_reverse(newtps));
3591 /* accumulate trackpoint copies in newtps, in reverse order */
3592 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3594 iter = g_list_next(iter);
3597 newlists = g_list_append(newlists, g_list_reverse(newtps));
3600 /* put lists of trackpoints into tracks */
3603 // Only bother updating if the split results in new tracks
3604 if (g_list_length (newlists) > 1) {
3609 tr = vik_track_new();
3610 tr->visible = track->visible;
3611 tr->trackpoints = (GList *)(iter->data);
3613 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3614 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3615 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3616 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3618 iter = g_list_next(iter);
3620 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3621 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3623 g_list_free(newlists);
3627 * Split a track by the number of points as specified by the user
3629 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3631 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3632 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3634 // Check valid track
3635 GList *trps = track->trackpoints;
3639 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3640 _("Split Every Nth Point"),
3641 _("Split on every Nth point:"),
3642 250, // Default value as per typical limited track capacity of various GPS devices
3646 // Was a valid number returned?
3652 GList *newlists = NULL;
3653 GList *newtps = NULL;
3658 /* accumulate trackpoint copies in newtps, in reverse order */
3659 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3661 if (count >= points) {
3662 /* flush accumulated trackpoints into new list */
3663 newlists = g_list_append(newlists, g_list_reverse(newtps));
3667 iter = g_list_next(iter);
3670 // If there is a remaining chunk put that into the new split list
3671 // This may well be the whole track if no split points were encountered
3673 newlists = g_list_append(newlists, g_list_reverse(newtps));
3676 /* put lists of trackpoints into tracks */
3679 // Only bother updating if the split results in new tracks
3680 if (g_list_length (newlists) > 1) {
3685 tr = vik_track_new();
3686 tr->visible = track->visible;
3687 tr->trackpoints = (GList *)(iter->data);
3689 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3690 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3692 iter = g_list_next(iter);
3694 // Remove original track and then update the display
3695 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3696 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3698 g_list_free(newlists);
3701 /* end of split/merge routines */
3706 static void trw_layer_reverse ( gpointer pass_along[6] )
3708 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3709 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3711 // Check valid track
3712 GList *trps = track->trackpoints;
3716 vik_track_reverse ( track );
3718 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3722 * Similar to trw_layer_enum_item, but this uses a sorted method
3724 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3726 GList **list = (GList**)udata;
3727 //*list = g_list_prepend(*all, key); //unsorted method
3728 // Sort named list alphabetically
3729 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3735 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3737 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3739 // Sort list alphabetically for better presentation
3740 g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3743 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3747 // Get list of items to delete from the user
3748 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3751 _("Delete Selection"),
3752 _("Select tracks to delete"));
3755 // Delete requested tracks
3756 // since specificly requested, IMHO no need for extra confirmation
3757 if ( delete_list ) {
3759 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3760 vik_trw_layer_delete_track(vtl, l->data);
3762 g_list_free(delete_list);
3763 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3770 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3772 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3775 // Sort list alphabetically for better presentation
3776 g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3778 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3782 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3784 // Get list of items to delete from the user
3785 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3788 _("Delete Selection"),
3789 _("Select waypoints to delete"));
3792 // Delete requested waypoints
3793 // since specificly requested, IMHO no need for extra confirmation
3794 if ( delete_list ) {
3796 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3797 vik_trw_layer_delete_waypoint(vtl, l->data);
3799 g_list_free(delete_list);
3800 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3805 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3807 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3809 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3812 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3814 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3815 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3819 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3821 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3826 if (strcmp(newname, sublayer) == 0 )
3829 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3830 if (g_hash_table_lookup( l->waypoints, newname))
3832 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3837 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3838 g_hash_table_steal ( l->waypoints_iters, sublayer );
3840 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3841 highest_wp_number_remove_wp(l, sublayer);
3842 g_hash_table_remove ( l->waypoints, sublayer );
3844 rv = g_strdup(newname);
3846 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3848 highest_wp_number_add_wp(l, rv);
3849 g_hash_table_insert ( l->waypoints, rv, wp );
3850 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3852 /* it hasn't been updated yet so we pass new name */
3853 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3854 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3857 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3860 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3867 if (strcmp(newname, sublayer) == 0)
3870 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3871 if (g_hash_table_lookup( l->tracks, newname))
3873 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3878 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3879 g_hash_table_steal ( l->tracks, sublayer );
3881 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3882 g_hash_table_steal ( l->tracks_iters, sublayer );
3884 rv = g_strdup(newname);
3886 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3888 g_hash_table_insert ( l->tracks, rv, tr );
3889 g_hash_table_insert ( l->tracks_iters, rv, iter );
3891 /* don't forget about current_tp_track_name, update that too */
3892 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3894 l->current_tp_track_name = rv;
3896 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3898 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3899 l->last_tp_track_name = rv;
3901 g_free ( orig_key );
3903 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3904 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3907 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3913 static gboolean is_valid_geocache_name ( gchar *str )
3915 gint len = strlen ( str );
3916 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]));
3919 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3921 gchar *track_name = (gchar *) pass_along[3];
3922 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3923 a_acquire_set_filter_track ( tr, track_name );
3926 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3928 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3929 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3932 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3934 gchar *track_name = (gchar *) pass_along[3];
3935 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3937 gchar *escaped = uri_escape ( tr->comment );
3938 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3939 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3945 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3946 /* viewpoint is now available instead */
3947 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3949 static gpointer pass_along[6];
3951 gboolean rv = FALSE;
3954 pass_along[1] = vlp;
3955 pass_along[2] = GINT_TO_POINTER (subtype);
3956 pass_along[3] = sublayer;
3957 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
3958 pass_along[5] = vvp;
3960 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3964 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3966 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3967 gtk_widget_show ( item );
3969 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3970 VikTrwLayer *vtl = l;
3971 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3972 if (tr && tr->property_dialog)
3973 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3976 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3977 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3978 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3979 gtk_widget_show ( item );
3981 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3982 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3983 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3984 gtk_widget_show ( item );
3986 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3988 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3989 gtk_widget_show ( item );
3991 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3993 gboolean separator_created = FALSE;
3995 /* could be a right-click using the tool */
3996 if ( vlp != NULL ) {
3997 item = gtk_menu_item_new ();
3998 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3999 gtk_widget_show ( item );
4001 separator_created = TRUE;
4003 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4004 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4006 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4007 gtk_widget_show ( item );
4010 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
4012 if ( !separator_created ) {
4013 item = gtk_menu_item_new ();
4014 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4015 gtk_widget_show ( item );
4016 separator_created = TRUE;
4019 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
4020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
4021 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4022 gtk_widget_show ( item );
4025 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4027 if ( wp && wp->image )
4029 if ( !separator_created ) {
4030 item = gtk_menu_item_new ();
4031 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4032 gtk_widget_show ( item );
4033 separator_created = TRUE;
4036 // Set up image paramater
4037 pass_along[5] = wp->image;
4039 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
4040 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
4041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
4042 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4043 gtk_widget_show ( item );
4045 #ifdef VIK_CONFIG_GEOTAG
4046 GtkWidget *geotag_submenu = gtk_menu_new ();
4047 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
4048 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
4049 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4050 gtk_widget_show ( item );
4051 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
4053 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
4054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
4055 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4056 gtk_widget_show ( item );
4058 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
4059 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
4060 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4061 gtk_widget_show ( item );
4068 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
4071 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
4072 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4074 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4075 gtk_widget_show ( item );
4078 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
4080 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
4081 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4083 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4084 gtk_widget_show ( item );
4086 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4087 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4089 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4090 gtk_widget_show ( item );
4092 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
4093 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4095 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4096 gtk_widget_show ( item );
4098 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
4099 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4100 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4101 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4102 gtk_widget_show ( item );
4105 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
4109 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
4110 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4111 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4112 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4113 gtk_widget_show ( item );
4115 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
4116 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4117 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4118 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4119 gtk_widget_show ( item );
4121 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
4122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4124 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4125 gtk_widget_show ( item );
4128 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4130 GtkWidget *goto_submenu;
4131 item = gtk_menu_item_new ();
4132 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4133 gtk_widget_show ( item );
4135 goto_submenu = gtk_menu_new ();
4136 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4137 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4138 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4139 gtk_widget_show ( item );
4140 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
4142 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
4143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
4144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
4145 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4146 gtk_widget_show ( item );
4148 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
4149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
4151 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4152 gtk_widget_show ( item );
4154 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
4155 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
4156 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
4157 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4158 gtk_widget_show ( item );
4160 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
4161 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
4162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
4163 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4164 gtk_widget_show ( item );
4166 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4167 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4169 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4170 gtk_widget_show ( item );
4172 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4173 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4175 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4176 gtk_widget_show ( item );
4178 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4179 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4181 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4182 gtk_widget_show ( item );
4184 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4186 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4187 gtk_widget_show ( item );
4189 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4190 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4191 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4192 gtk_widget_show ( item );
4194 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4196 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4197 gtk_widget_show ( item );
4199 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4201 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4202 gtk_widget_show ( item );
4204 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
4205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
4206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
4207 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4208 gtk_widget_show ( item );
4210 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4212 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4213 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
4214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4215 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4216 gtk_widget_show ( item );
4219 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4220 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
4221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4222 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4223 gtk_widget_show ( item );
4225 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4226 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
4228 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4229 gtk_widget_show ( item );
4231 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
4232 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4233 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4234 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4235 gtk_widget_show ( item );
4237 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4238 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
4239 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4240 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4241 gtk_widget_show ( item );
4243 #ifdef VIK_CONFIG_OPENSTREETMAP
4244 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4246 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4247 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4248 gtk_widget_show ( item );
4251 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
4253 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4254 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4257 gtk_widget_show ( item );
4260 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4261 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4262 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4263 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4264 gtk_widget_show ( item );
4266 /* ATM This function is only available via the layers panel, due to needing a vlp */
4268 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4269 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4270 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4272 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4273 gtk_widget_show ( item );
4277 #ifdef VIK_CONFIG_GEOTAG
4278 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
4280 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4281 gtk_widget_show ( item );
4284 // Only show on viewport popmenu when a trackpoint is selected
4285 if ( ! vlp && l->current_tpl ) {
4287 item = gtk_menu_item_new ();
4288 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4289 gtk_widget_show ( item );
4291 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4292 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4294 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4295 gtk_widget_show ( item );
4303 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4306 if (!vtl->current_tpl)
4308 if (!vtl->current_tpl->next)
4311 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4312 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4314 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4317 VikTrackpoint *tp_new = vik_trackpoint_new();
4318 struct LatLon ll_current, ll_next;
4319 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4320 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4322 /* main positional interpolation */
4323 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4324 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4326 /* Now other properties that can be interpolated */
4327 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4329 if (tp_current->has_timestamp && tp_next->has_timestamp) {
4330 /* Note here the division is applied to each part, then added
4331 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4332 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4333 tp_new->has_timestamp = TRUE;
4336 if (tp_current->speed != NAN && tp_next->speed != NAN)
4337 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4339 /* TODO - improve interpolation of course, as it may not be correct.
4340 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4341 [similar applies if value is in radians] */
4342 if (tp_current->course != NAN && tp_next->course != NAN)
4343 tp_new->speed = (tp_current->course + tp_next->course)/2;
4345 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4347 /* Insert new point into the trackpoints list after the current TP */
4348 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4349 gint index = g_list_index ( tr->trackpoints, tp_current );
4351 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4356 /* to be called when last_tpl no long exists. */
4357 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4359 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4360 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4361 vtl->last_tpl = NULL;
4362 vtl->last_tp_track_name = NULL;
4365 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4371 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4375 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4377 if ( vtl->current_tpl )
4379 vtl->current_tpl = NULL;
4380 vtl->current_tp_track_name = NULL;
4381 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4385 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4387 g_assert ( vtl->tpwin != NULL );
4388 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4389 trw_layer_cancel_current_tp ( vtl, TRUE );
4391 if ( vtl->current_tpl == NULL )
4394 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4396 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4397 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4399 VikTrack *tr = vik_track_new ();
4400 GList *newglist = g_list_alloc ();
4401 newglist->prev = NULL;
4402 newglist->next = vtl->current_tpl->next;
4403 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4404 tr->trackpoints = newglist;
4406 vtl->current_tpl->next->prev = newglist; /* end old track here */
4407 vtl->current_tpl->next = NULL;
4409 vtl->current_tpl = newglist; /* change tp to first of new track. */
4410 vtl->current_tp_track_name = name;
4412 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4416 vik_trw_layer_add_track ( vtl, name, tr );
4417 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4420 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4422 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4424 g_assert(tr != NULL);
4426 /* can't join with a non-existent trackpoint */
4427 vtl->last_tpl = NULL;
4428 vtl->last_tp_track_name = NULL;
4430 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4432 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4433 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4435 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4437 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4438 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4440 trw_layer_cancel_last_tp ( vtl );
4442 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4443 g_list_free_1 ( vtl->current_tpl );
4444 vtl->current_tpl = new_tpl;
4445 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4449 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4450 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4451 g_list_free_1 ( vtl->current_tpl );
4452 trw_layer_cancel_current_tp ( vtl, FALSE );
4455 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4457 vtl->last_tpl = vtl->current_tpl;
4458 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4459 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4461 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4463 vtl->last_tpl = vtl->current_tpl;
4464 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4465 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4467 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4469 // Check tracks exist and are different before joining
4470 if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4473 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4474 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4476 VikTrack *tr_first = tr1, *tr_last = tr2;
4480 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4481 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4482 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4483 vik_track_reverse ( tr1 );
4484 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4489 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4491 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. */
4492 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4493 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4494 tr2->trackpoints = NULL;
4496 tmp = vtl->current_tp_track_name;
4498 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4499 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4501 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4502 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4503 vik_trw_layer_delete_track ( vtl, tmp );
4505 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4506 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4508 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4510 trw_layer_insert_tp_after_current_tp ( vtl );
4511 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4513 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4514 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4517 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4521 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4522 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4523 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4524 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4525 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4527 if ( vtl->current_tpl )
4528 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4529 /* set layer name and TP data */
4532 /***************************************************************************
4534 ***************************************************************************/
4536 /*** Utility data structures and functions ****/
4540 gint closest_x, closest_y;
4541 gchar *closest_wp_name;
4542 VikWaypoint *closest_wp;
4548 gint closest_x, closest_y;
4549 gchar *closest_track_name;
4550 VikTrackpoint *closest_tp;
4555 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4561 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4563 // If waypoint has an image then use the image size to select
4565 gint slackx, slacky;
4566 slackx = wp->image_width / 2;
4567 slacky = wp->image_height / 2;
4569 if ( x <= params->x + slackx && x >= params->x - slackx
4570 && y <= params->y + slacky && y >= params->y - slacky ) {
4571 params->closest_wp_name = name;
4572 params->closest_wp = wp;
4573 params->closest_x = x;
4574 params->closest_y = y;
4577 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4578 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
4579 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4581 params->closest_wp_name = name;
4582 params->closest_wp = wp;
4583 params->closest_x = x;
4584 params->closest_y = y;
4588 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4590 GList *tpl = t->trackpoints;
4599 tp = VIK_TRACKPOINT(tpl->data);
4601 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4603 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4604 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
4605 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4607 params->closest_track_name = name;
4608 params->closest_tp = tp;
4609 params->closest_tpl = tpl;
4610 params->closest_x = x;
4611 params->closest_y = y;
4617 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4619 TPSearchParams params;
4623 params.closest_track_name = NULL;
4624 params.closest_tp = NULL;
4625 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4626 return params.closest_tp;
4629 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4631 WPSearchParams params;
4635 params.closest_wp = NULL;
4636 params.closest_wp_name = NULL;
4637 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4638 return params.closest_wp;
4642 // Some forward declarations
4643 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4644 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4645 static void marker_end_move ( tool_ed_t *t );
4648 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4652 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4654 // Here always allow snapping back to the original location
4655 // this is useful when one decides not to move the thing afterall
4656 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4659 if ( event->state & GDK_CONTROL_MASK )
4661 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4663 new_coord = tp->coord;
4667 if ( event->state & GDK_SHIFT_MASK )
4669 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4671 new_coord = wp->coord;
4675 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4677 marker_moveto ( t, x, y );
4684 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4686 if ( t->holding && event->button == 1 )
4689 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4692 if ( event->state & GDK_CONTROL_MASK )
4694 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4696 new_coord = tp->coord;
4700 if ( event->state & GDK_SHIFT_MASK )
4702 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4704 new_coord = wp->coord;
4707 marker_end_move ( t );
4709 // Determine if working on a waypoint or a trackpoint
4710 if ( t->is_waypoint )
4711 vtl->current_wp->coord = new_coord;
4713 if ( vtl->current_tpl ) {
4714 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4717 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4719 // Don't really know what this is for but seems like it might be handy...
4720 /* can't join with itself! */
4721 trw_layer_cancel_last_tp ( vtl );
4726 vtl->current_wp = NULL;
4727 vtl->current_wp_name = NULL;
4728 trw_layer_cancel_current_tp ( vtl, FALSE );
4730 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4737 Returns true if a waypoint or track is found near the requested event position for this particular layer
4738 The item found is automatically selected
4739 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4741 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4743 if ( event->button != 1 )
4746 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4749 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4752 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4754 if (vtl->waypoints_visible) {
4755 WPSearchParams wp_params;
4756 wp_params.vvp = vvp;
4757 wp_params.x = event->x;
4758 wp_params.y = event->y;
4759 wp_params.closest_wp_name = NULL;
4760 wp_params.closest_wp = NULL;
4762 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4764 if ( wp_params.closest_wp ) {
4767 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4769 // Too easy to move it so must be holding shift to start immediately moving it
4770 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
4771 if ( event->state & GDK_SHIFT_MASK ||
4772 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
4773 // Put into 'move buffer'
4774 // NB vvp & vw already set in tet
4775 tet->vtl = (gpointer)vtl;
4776 tet->is_waypoint = TRUE;
4778 marker_begin_move (tet, event->x, event->y);
4781 vtl->current_wp = wp_params.closest_wp;
4782 vtl->current_wp_name = wp_params.closest_wp_name;
4784 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4790 if (vtl->tracks_visible) {
4791 TPSearchParams tp_params;
4792 tp_params.vvp = vvp;
4793 tp_params.x = event->x;
4794 tp_params.y = event->y;
4795 tp_params.closest_track_name = NULL;
4796 tp_params.closest_tp = NULL;
4798 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4800 if ( tp_params.closest_tp ) {
4802 // Always select + highlight the track
4803 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4805 tet->is_waypoint = FALSE;
4807 // Select the Trackpoint
4808 // Can move it immediately when control held or it's the previously selected tp
4809 if ( event->state & GDK_CONTROL_MASK ||
4810 vtl->current_tpl == tp_params.closest_tpl ) {
4811 // Put into 'move buffer'
4812 // NB vvp & vw already set in tet
4813 tet->vtl = (gpointer)vtl;
4814 marker_begin_move (tet, event->x, event->y);
4817 vtl->current_tpl = tp_params.closest_tpl;
4818 vtl->current_tp_track_name = tp_params.closest_track_name;
4820 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
4823 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4825 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4830 /* these aren't the droids you're looking for */
4831 vtl->current_wp = NULL;
4832 vtl->current_wp_name = NULL;
4833 trw_layer_cancel_current_tp ( vtl, FALSE );
4836 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
4841 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4843 if ( event->button != 3 )
4846 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4849 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4852 /* Post menu for the currently selected item */
4854 /* See if a track is selected */
4855 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4856 if ( track && track->visible ) {
4858 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4860 if ( vtl->track_right_click_menu )
4861 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4863 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4865 trw_layer_sublayer_add_menu_items ( vtl,
4866 vtl->track_right_click_menu,
4868 VIK_TRW_LAYER_SUBLAYER_TRACK,
4869 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4870 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4873 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4879 /* See if a waypoint is selected */
4880 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4881 if ( waypoint && waypoint->visible ) {
4882 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4884 if ( vtl->wp_right_click_menu )
4885 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4887 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4888 trw_layer_sublayer_add_menu_items ( vtl,
4889 vtl->wp_right_click_menu,
4891 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4892 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4893 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4895 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4904 /* background drawing hook, to be passed the viewport */
4905 static gboolean tool_sync_done = TRUE;
4907 static gboolean tool_sync(gpointer data)
4909 VikViewport *vvp = data;
4910 gdk_threads_enter();
4911 vik_viewport_sync(vvp);
4912 tool_sync_done = TRUE;
4913 gdk_threads_leave();
4917 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4920 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4921 gdk_gc_set_function ( t->gc, GDK_INVERT );
4922 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4923 vik_viewport_sync(t->vvp);
4928 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4930 VikViewport *vvp = t->vvp;
4931 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4932 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4936 if (tool_sync_done) {
4937 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4938 tool_sync_done = FALSE;
4942 static void marker_end_move ( tool_ed_t *t )
4944 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4945 g_object_unref ( t->gc );
4949 /*** Edit waypoint ****/
4951 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4953 tool_ed_t *t = g_new(tool_ed_t, 1);
4959 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4961 WPSearchParams params;
4962 tool_ed_t *t = data;
4963 VikViewport *vvp = t->vvp;
4965 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4972 if ( !vtl->vl.visible || !vtl->waypoints_visible )
4975 if ( vtl->current_wp && vtl->current_wp->visible )
4977 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
4979 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
4981 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
4982 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
4984 if ( event->button == 3 )
4985 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4987 marker_begin_move(t, event->x, event->y);
4994 params.x = event->x;
4995 params.y = event->y;
4996 params.closest_wp_name = NULL;
4997 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4998 params.closest_wp = NULL;
4999 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5000 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5002 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5003 marker_begin_move(t, event->x, event->y);
5004 g_critical("shouldn't be here");
5007 else if ( params.closest_wp )
5009 if ( event->button == 3 )
5010 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5012 vtl->waypoint_rightclick = FALSE;
5014 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
5016 vtl->current_wp = params.closest_wp;
5017 vtl->current_wp_name = params.closest_wp_name;
5019 /* could make it so don't update if old WP is off screen and new is null but oh well */
5020 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5024 vtl->current_wp = NULL;
5025 vtl->current_wp_name = NULL;
5026 vtl->waypoint_rightclick = FALSE;
5027 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5031 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5033 tool_ed_t *t = data;
5034 VikViewport *vvp = t->vvp;
5036 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5041 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5044 if ( event->state & GDK_CONTROL_MASK )
5046 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5048 new_coord = tp->coord;
5052 if ( event->state & GDK_SHIFT_MASK )
5054 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5055 if ( wp && wp != vtl->current_wp )
5056 new_coord = wp->coord;
5061 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5063 marker_moveto ( t, x, y );
5070 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5072 tool_ed_t *t = data;
5073 VikViewport *vvp = t->vvp;
5075 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5078 if ( t->holding && event->button == 1 )
5081 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5084 if ( event->state & GDK_CONTROL_MASK )
5086 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5088 new_coord = tp->coord;
5092 if ( event->state & GDK_SHIFT_MASK )
5094 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5095 if ( wp && wp != vtl->current_wp )
5096 new_coord = wp->coord;
5099 marker_end_move ( t );
5101 vtl->current_wp->coord = new_coord;
5102 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5105 /* PUT IN RIGHT PLACE!!! */
5106 if ( event->button == 3 && vtl->waypoint_rightclick )
5108 if ( vtl->wp_right_click_menu )
5109 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5110 if ( vtl->current_wp ) {
5111 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5112 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 );
5113 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5115 vtl->waypoint_rightclick = FALSE;
5120 /**** Begin track ***/
5121 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5126 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5128 vtl->current_track = NULL;
5129 return tool_new_track_click ( vtl, event, vvp );
5132 /*** New track ****/
5134 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5142 gint x1,y1,x2,y2,x3,y3;
5144 } new_track_move_passalong_t;
5146 /* sync and undraw, but only when we have time */
5147 static gboolean ct_sync ( gpointer passalong )
5149 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5151 vik_viewport_sync ( p->vvp );
5152 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5153 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5154 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);
5155 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5157 g_free ( (gpointer) p->str ) ;
5158 p->vtl->ct_sync_done = TRUE;
5163 static const gchar* distance_string (gdouble distance)
5167 /* draw label with distance */
5168 vik_units_distance_t dist_units = a_vik_get_units_distance ();
5169 switch (dist_units) {
5170 case VIK_UNITS_DISTANCE_MILES:
5171 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5172 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5173 } else if (distance < 1609.4) {
5174 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5176 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5180 // VIK_UNITS_DISTANCE_KILOMETRES
5181 if (distance >= 1000 && distance < 100000) {
5182 g_sprintf(str, "%3.2f km", distance/1000.0);
5183 } else if (distance < 1000) {
5184 g_sprintf(str, "%d m", (int)distance);
5186 g_sprintf(str, "%d km", (int)distance/1000);
5190 return g_strdup (str);
5194 * Actually set the message in statusbar
5196 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5198 // Only show elevation data when track has some elevation properties
5199 gchar str_gain_loss[64];
5200 str_gain_loss[0] = '\0';
5202 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5203 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5204 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5206 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5209 // Write with full gain/loss information
5210 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5211 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5216 * Figure out what information should be set in the statusbar and then write it
5218 static void update_statusbar ( VikTrwLayer *vtl )
5220 // Get elevation data
5221 gdouble elev_gain, elev_loss;
5222 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5224 /* Find out actual distance of current track */
5225 gdouble distance = vik_track_get_length (vtl->current_track);
5226 const gchar *str = distance_string (distance);
5228 statusbar_write (str, elev_gain, elev_loss, vtl);
5230 g_free ((gpointer)str);
5234 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5236 /* if we haven't sync'ed yet, we don't have time to do more. */
5237 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5238 GList *iter = vtl->current_track->trackpoints;
5239 new_track_move_passalong_t *passalong;
5242 while ( iter->next )
5244 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5245 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5246 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5248 /* Find out actual distance of current track */
5249 gdouble distance = vik_track_get_length (vtl->current_track);
5251 // Now add distance to where the pointer is //
5254 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5255 vik_coord_to_latlon ( &coord, &ll );
5256 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5258 // Get elevation data
5259 gdouble elev_gain, elev_loss;
5260 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5262 // Adjust elevation data (if available) for the current pointer position
5264 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5265 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5266 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5267 // Adjust elevation of last track point
5268 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5270 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5273 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5277 const gchar *str = distance_string (distance);
5279 /* offset from cursor a bit */
5282 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5283 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5285 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5287 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5288 passalong->vtl = vtl;
5289 passalong->vvp = vvp;
5292 passalong->x2 = event->x;
5293 passalong->y2 = event->y;
5296 passalong->str = str;
5298 // Update statusbar with full gain/loss information
5299 statusbar_write (str, elev_gain, elev_loss, vtl);
5301 /* this will sync and undraw when we have time to */
5302 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5303 vtl->ct_sync_done = FALSE;
5304 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5306 return VIK_LAYER_TOOL_ACK;
5309 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5311 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5312 vtl->current_track = NULL;
5313 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5315 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5317 if ( vtl->current_track->trackpoints )
5319 GList *last = g_list_last(vtl->current_track->trackpoints);
5320 g_free ( last->data );
5321 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5324 update_statusbar ( vtl );
5326 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5332 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5336 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5339 if ( event->button == 3 && vtl->current_track )
5342 if ( vtl->current_track->trackpoints )
5344 GList *last = g_list_last(vtl->current_track->trackpoints);
5345 g_free ( last->data );
5346 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5348 update_statusbar ( vtl );
5350 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5354 if ( event->type == GDK_2BUTTON_PRESS )
5356 /* subtract last (duplicate from double click) tp then end */
5357 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5359 GList *last = g_list_last(vtl->current_track->trackpoints);
5360 g_free ( last->data );
5361 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5362 /* undo last, then end */
5363 vtl->current_track = NULL;
5365 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5369 if ( ! vtl->current_track )
5371 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5372 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5374 vtl->current_track = vik_track_new();
5375 vtl->current_track->visible = TRUE;
5376 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5378 /* incase it was created by begin track */
5379 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5384 tp = vik_trackpoint_new();
5385 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5387 /* snap to other TP */
5388 if ( event->state & GDK_CONTROL_MASK )
5390 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5392 tp->coord = other_tp->coord;
5395 tp->newsegment = FALSE;
5396 tp->has_timestamp = FALSE;
5398 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5399 /* Auto attempt to get elevation from DEM data (if it's available) */
5400 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5402 vtl->ct_x1 = vtl->ct_x2;
5403 vtl->ct_y1 = vtl->ct_y2;
5404 vtl->ct_x2 = event->x;
5405 vtl->ct_y2 = event->y;
5407 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5411 /*** New waypoint ****/
5413 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5418 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5421 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5423 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5424 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5425 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5430 /*** Edit trackpoint ****/
5432 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5434 tool_ed_t *t = g_new(tool_ed_t, 1);
5440 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5442 tool_ed_t *t = data;
5443 VikViewport *vvp = t->vvp;
5444 TPSearchParams params;
5445 /* OUTDATED DOCUMENTATION:
5446 find 5 pixel range on each side. then put these UTM, and a pointer
5447 to the winning track name (and maybe the winning track itself), and a
5448 pointer to the winning trackpoint, inside an array or struct. pass
5449 this along, do a foreach on the tracks which will do a foreach on the
5452 params.x = event->x;
5453 params.y = event->y;
5454 params.closest_track_name = NULL;
5455 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5456 params.closest_tp = NULL;
5458 if ( event->button != 1 )
5461 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5464 if ( !vtl->vl.visible || !vtl->tracks_visible )
5467 if ( vtl->current_tpl )
5469 /* 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.) */
5470 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5471 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5473 g_assert ( current_tr );
5475 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5477 if ( current_tr->visible &&
5478 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5479 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5480 marker_begin_move ( t, event->x, event->y );
5484 vtl->last_tpl = vtl->current_tpl;
5485 vtl->last_tp_track_name = vtl->current_tp_track_name;
5488 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5490 if ( params.closest_tp )
5492 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5493 vtl->current_tpl = params.closest_tpl;
5494 vtl->current_tp_track_name = params.closest_track_name;
5495 trw_layer_tpwin_init ( vtl );
5496 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5497 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5501 /* these aren't the droids you're looking for */
5505 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5507 tool_ed_t *t = data;
5508 VikViewport *vvp = t->vvp;
5510 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5516 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5519 if ( event->state & GDK_CONTROL_MASK )
5521 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5522 if ( tp && tp != vtl->current_tpl->data )
5523 new_coord = tp->coord;
5525 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5528 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5529 marker_moveto ( t, x, y );
5537 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5539 tool_ed_t *t = data;
5540 VikViewport *vvp = t->vvp;
5542 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5544 if ( event->button != 1)
5549 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5552 if ( event->state & GDK_CONTROL_MASK )
5554 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5555 if ( tp && tp != vtl->current_tpl->data )
5556 new_coord = tp->coord;
5559 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5561 marker_end_move ( t );
5563 /* diff dist is diff from orig */
5565 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5566 /* can't join with itself! */
5567 trw_layer_cancel_last_tp ( vtl );
5569 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5576 /*** Route Finder ***/
5577 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5582 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5585 if ( !vtl ) return FALSE;
5586 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5587 if ( event->button == 3 && vtl->route_finder_current_track ) {
5589 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5591 vtl->route_finder_coord = *new_end;
5593 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5594 /* remove last ' to:...' */
5595 if ( vtl->route_finder_current_track->comment ) {
5596 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5597 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5598 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5599 last_to - vtl->route_finder_current_track->comment - 1);
5600 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5605 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5606 struct LatLon start, end;
5607 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5608 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5611 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5612 vik_coord_to_latlon ( &(tmp), &end );
5613 vtl->route_finder_coord = tmp; /* for continuations */
5615 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5616 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5617 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
5619 vtl->route_finder_check_added_track = TRUE;
5620 vtl->route_finder_started = FALSE;
5623 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5624 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5625 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5626 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5627 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5628 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5631 /* see if anything was done -- a track was added or appended to */
5632 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5635 tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5638 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5640 vtl->route_finder_current_track = tr;
5642 g_free ( vtl->route_finder_added_track_name );
5643 vtl->route_finder_added_track_name = NULL;
5644 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5645 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5646 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5647 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5649 vtl->route_finder_check_added_track = FALSE;
5650 vtl->route_finder_append = FALSE;
5652 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5654 vtl->route_finder_started = TRUE;
5655 vtl->route_finder_coord = tmp;
5656 vtl->route_finder_current_track = NULL;
5661 /*** Show picture ****/
5663 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5668 /* Params are: vvp, event, last match found or NULL */
5669 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
5671 if ( wp->image && wp->visible )
5673 gint x, y, slackx, slacky;
5674 GdkEventButton *event = (GdkEventButton *) params[1];
5676 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5677 slackx = wp->image_width / 2;
5678 slacky = wp->image_height / 2;
5679 if ( x <= event->x + slackx && x >= event->x - slackx
5680 && y <= event->y + slacky && y >= event->y - slacky )
5682 params[2] = wp->image; /* we've found a match. however continue searching
5683 * since we want to find the last match -- that
5684 * is, the match that was drawn last. */
5689 static void trw_layer_show_picture ( gpointer pass_along[6] )
5691 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5693 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5696 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5697 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
5698 g_free ( quoted_file );
5699 if ( ! g_spawn_command_line_async ( cmd, &err ) )
5701 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() );
5702 g_error_free ( err );
5705 #endif /* WINDOWS */
5708 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5710 gpointer params[3] = { vvp, event, NULL };
5711 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5713 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5716 static gpointer pass_along[6];
5717 pass_along[0] = vtl;
5718 pass_along[5] = params[2];
5719 trw_layer_show_picture ( pass_along );
5720 return TRUE; /* found a match */
5723 return FALSE; /* go through other layers, searching for a match */
5726 /***************************************************************************
5728 ***************************************************************************/
5734 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5736 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5737 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5740 /* Structure for thumbnail creating data used in the background thread */
5742 VikTrwLayer *vtl; // Layer needed for redrawing
5743 GSList *pics; // Image list
5744 } thumbnail_create_thread_data;
5746 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5748 guint total = g_slist_length(tctd->pics), done = 0;
5749 while ( tctd->pics )
5751 a_thumbnails_create ( (gchar *) tctd->pics->data );
5752 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5754 return -1; /* Abort thread */
5756 tctd->pics = tctd->pics->next;
5759 // Redraw to show the thumbnails as they are now created
5760 if ( IS_VIK_LAYER(tctd->vtl) )
5761 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
5766 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5768 while ( tctd->pics )
5770 g_free ( tctd->pics->data );
5771 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5776 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5778 if ( ! vtl->has_verified_thumbnails )
5780 GSList *pics = NULL;
5781 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5784 gint len = g_slist_length ( pics );
5785 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5786 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5789 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5791 (vik_thr_func) create_thumbnails_thread,
5793 (vik_thr_free_func) thumbnail_create_thread_free,
5801 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5803 return vtl->coord_mode;
5808 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5810 vik_coord_convert ( &(wp->coord), *dest_mode );
5813 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5815 vik_track_convert ( tr, *dest_mode );
5818 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5820 if ( vtl->coord_mode != dest_mode )
5822 vtl->coord_mode = dest_mode;
5823 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5824 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5828 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5830 return g_hash_table_lookup ( vtl->waypoints, name );
5833 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5835 return g_hash_table_lookup ( vtl->tracks, name );
5838 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5840 vtl->menu_selection = selection;
5843 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5845 return (vtl->menu_selection);
5848 /* ----------- Downloading maps along tracks --------------- */
5850 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5852 /* TODO: calculating based on current size of viewport */
5853 const gdouble w_at_zoom_0_125 = 0.0013;
5854 const gdouble h_at_zoom_0_125 = 0.0011;
5855 gdouble zoom_factor = zoom_level/0.125;
5857 wh->lat = h_at_zoom_0_125 * zoom_factor;
5858 wh->lon = w_at_zoom_0_125 * zoom_factor;
5860 return 0; /* all OK */
5863 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5865 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5866 (dist->lat >= ABS(to->north_south - from->north_south)))
5869 VikCoord *coord = g_malloc(sizeof(VikCoord));
5870 coord->mode = VIK_COORD_LATLON;
5872 if (ABS(gradient) < 1) {
5873 if (from->east_west > to->east_west)
5874 coord->east_west = from->east_west - dist->lon;
5876 coord->east_west = from->east_west + dist->lon;
5877 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5879 if (from->north_south > to->north_south)
5880 coord->north_south = from->north_south - dist->lat;
5882 coord->north_south = from->north_south + dist->lat;
5883 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5889 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5891 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5892 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5894 VikCoord *next = from;
5896 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5898 list = g_list_prepend(list, next);
5904 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5906 typedef struct _Rect {
5911 #define GLRECT(iter) ((Rect *)((iter)->data))
5914 GList *rects_to_download = NULL;
5917 if (get_download_area_width(vvp, zoom_level, &wh))
5920 GList *iter = tr->trackpoints;
5924 gboolean new_map = TRUE;
5925 VikCoord *cur_coord, tl, br;
5928 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5930 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5931 rect = g_malloc(sizeof(Rect));
5934 rect->center = *cur_coord;
5935 rects_to_download = g_list_prepend(rects_to_download, rect);
5940 gboolean found = FALSE;
5941 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5942 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
5953 GList *fillins = NULL;
5954 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
5955 /* seems that ATM the function get_next_coord works only for LATLON */
5956 if ( cur_coord->mode == VIK_COORD_LATLON ) {
5957 /* fill-ins for far apart points */
5958 GList *cur_rect, *next_rect;
5959 for (cur_rect = rects_to_download;
5960 (next_rect = cur_rect->next) != NULL;
5961 cur_rect = cur_rect->next) {
5962 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
5963 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
5964 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
5968 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
5971 GList *iter = fillins;
5973 cur_coord = (VikCoord *)(iter->data);
5974 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5975 rect = g_malloc(sizeof(Rect));
5978 rect->center = *cur_coord;
5979 rects_to_download = g_list_prepend(rects_to_download, rect);
5984 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5985 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
5989 for (iter = fillins; iter; iter = iter->next)
5991 g_list_free(fillins);
5993 if (rects_to_download) {
5994 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
5995 g_free(rect_iter->data);
5996 g_list_free(rects_to_download);
6000 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6003 gint selected_map, default_map;
6004 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6005 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6006 gint selected_zoom, default_zoom;
6010 VikTrwLayer *vtl = pass_along[0];
6011 VikLayersPanel *vlp = pass_along[1];
6012 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6013 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6015 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6016 int num_maps = g_list_length(vmls);
6019 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6023 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6024 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6026 gchar **np = map_names;
6027 VikMapsLayer **lp = map_layers;
6028 for (i = 0; i < num_maps; i++) {
6029 gboolean dup = FALSE;
6030 vml = (VikMapsLayer *)(vmls->data);
6031 for (j = 0; j < i; j++) { /* no duplicate allowed */
6032 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6039 *np++ = vik_maps_layer_get_map_label(vml);
6045 num_maps = lp - map_layers;
6047 for (default_map = 0; default_map < num_maps; default_map++) {
6048 /* TODO: check for parent layer's visibility */
6049 if (VIK_LAYER(map_layers[default_map])->visible)
6052 default_map = (default_map == num_maps) ? 0 : default_map;
6054 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6055 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6056 if (cur_zoom == zoom_vals[default_zoom])
6059 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6061 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6064 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6067 for (i = 0; i < num_maps; i++)
6068 g_free(map_names[i]);
6076 /**** lowest waypoint number calculation ***/
6077 static gint highest_wp_number_name_to_number(const gchar *name) {
6078 if ( strlen(name) == 3 ) {
6080 if ( n < 100 && name[0] != '0' )
6082 if ( n < 10 && name[0] != '0' )
6090 static void highest_wp_number_reset(VikTrwLayer *vtl)
6092 vtl->highest_wp_number = -1;
6095 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6097 /* if is bigger that top, add it */
6098 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6099 if ( new_wp_num > vtl->highest_wp_number )
6100 vtl->highest_wp_number = new_wp_num;
6103 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6105 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6106 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6107 if ( vtl->highest_wp_number == old_wp_num ) {
6109 vtl->highest_wp_number --;
6111 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6112 /* search down until we find something that *does* exist */
6114 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
6115 vtl->highest_wp_number --;
6116 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6121 /* get lowest unused number */
6122 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6125 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6127 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6128 return g_strdup(buf);