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) 2012, 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 16
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
89 #define VIK_TRW_LAYER_TRACK_GC_SLOW 13
90 #define VIK_TRW_LAYER_TRACK_GC_AVER 14
91 #define VIK_TRW_LAYER_TRACK_GC_FAST 15
93 #define DRAWMODE_BY_TRACK 0
94 #define DRAWMODE_BY_SPEED 1
95 #define DRAWMODE_ALL_BLACK 2
96 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
97 // as we are (re)calculating the colour for every point
102 /* this is how it knows when you click if you are clicking close to a trackpoint. */
103 #define TRACKPOINT_SIZE_APPROX 5
104 #define WAYPOINT_SIZE_APPROX 5
106 #define MIN_STOP_LENGTH 15
107 #define MAX_STOP_LENGTH 86400
108 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
109 /* this is multiplied by user-inputted value from 1-100. */
111 VIK_TRW_LAYER_SUBLAYER_TRACKS,
112 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
113 VIK_TRW_LAYER_SUBLAYER_TRACK,
114 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
117 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
119 struct _VikTrwLayer {
122 GHashTable *tracks_iters;
123 GHashTable *waypoints_iters;
124 GHashTable *waypoints;
125 GtkTreeIter waypoints_iter, tracks_iter;
126 gboolean tracks_visible, waypoints_visible;
129 guint8 drawelevation;
130 guint8 elevation_factor;
134 guint8 line_thickness;
135 guint8 bg_line_thickness;
139 gboolean wp_draw_symbols;
141 gdouble track_draw_speed_factor;
143 GdkGC *current_track_gc;
146 GdkGC *waypoint_text_gc;
147 GdkGC *waypoint_bg_gc;
148 GdkFont *waypoint_font;
149 VikTrack *current_track;
150 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
151 gboolean ct_sync_done;
154 VikCoordMode coord_mode;
156 /* wp editing tool */
157 VikWaypoint *current_wp;
158 gpointer current_wp_id;
160 gboolean waypoint_rightclick;
162 /* track editing tool */
164 VikTrack *current_tp_track;
165 gpointer current_tp_id;
166 VikTrwLayerTpwin *tpwin;
168 /* weird hack for joining tracks */
170 VikTrack *last_tp_track;
172 /* track editing tool -- more specifically, moving tps */
175 /* route finder tool */
176 gboolean route_finder_started;
177 VikCoord route_finder_coord;
178 gboolean route_finder_check_added_track;
179 VikTrack *route_finder_added_track;
180 VikTrack *route_finder_current_track;
181 gboolean route_finder_append;
188 guint16 image_cache_size;
190 /* for waypoint text */
191 PangoLayout *wplabellayout;
193 gboolean has_verified_thumbnails;
195 GtkMenu *wp_right_click_menu;
196 GtkMenu *track_right_click_menu;
199 VikStdLayerMenuItem menu_selection;
201 gint highest_wp_number;
204 /* A caached waypoint image. */
207 gchar *image; /* filename */
210 struct DrawingParams {
214 guint16 width, height;
215 const VikCoord *center;
217 gboolean one_zone, lat_lon;
218 gdouble ce1, ce2, cn1, cn2;
221 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
223 static void trw_layer_delete_item ( gpointer pass_along[6] );
224 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
225 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
227 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
228 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
229 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
231 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
232 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
234 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
235 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
237 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
238 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
239 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
240 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
241 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
242 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
243 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
244 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
245 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
246 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
247 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
248 static void trw_layer_reverse ( gpointer pass_along[6] );
249 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
250 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
251 static void trw_layer_show_picture ( gpointer pass_along[6] );
253 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
254 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
255 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
256 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
257 static void trw_layer_new_wp ( gpointer lav[2] );
258 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
259 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
260 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
261 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
262 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
263 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
264 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
265 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
266 #ifdef VIK_CONFIG_GEOTAG
267 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
268 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
269 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
270 static void trw_layer_geotagging ( gpointer lav[2] );
272 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
273 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
274 #ifdef VIK_CONFIG_OPENSTREETMAP
275 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
277 #ifdef VIK_CONFIG_GEOCACHES
278 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
280 #ifdef VIK_CONFIG_GEOTAG
281 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
285 static void trw_layer_properties_item ( gpointer pass_along[7] );
286 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
287 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
289 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
290 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
291 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
293 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
294 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
295 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
296 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
297 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
299 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
300 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
301 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
302 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
303 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
304 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
305 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
306 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
307 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
308 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
309 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
310 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
311 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
312 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
313 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
314 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
315 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
316 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
317 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
318 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
321 static void cached_pixbuf_free ( CachedPixbuf *cp );
322 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
324 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
325 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
327 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
328 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
329 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
331 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
332 static void highest_wp_number_reset(VikTrwLayer *vtl);
333 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
334 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
336 // Note for the following tool GtkRadioActionEntry texts:
337 // the very first text value is an internal name not displayed anywhere
338 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
339 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
340 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
341 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
342 static VikToolInterface trw_layer_tools[] = {
343 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
344 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
345 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
347 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
348 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
349 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
350 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
352 { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "<control><shift>B", N_("Begin Track"), 0 },
353 (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
354 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
356 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
357 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
358 (VikToolMouseFunc) tool_edit_waypoint_click,
359 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
360 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
362 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
363 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
364 (VikToolMouseFunc) tool_edit_trackpoint_click,
365 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
366 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
368 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
369 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
370 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
372 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
373 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
374 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
376 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
378 /****** PARAMETERS ******/
380 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
381 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
383 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
384 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
387 static VikLayerParamScale params_scales[] = {
388 /* min max step digits */
389 { 1, 10, 1, 0 }, /* line_thickness */
390 { 0, 100, 1, 0 }, /* track draw speed factor */
391 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
392 /* 5 * step == how much to turn */
393 { 16, 128, 3.2, 0 }, /* image_size */
394 { 0, 255, 5, 0 }, /* image alpha */
395 { 5, 500, 5, 0 }, /* image cache_size */
396 { 0, 8, 1, 0 }, /* image cache_size */
397 { 1, 64, 1, 0 }, /* wpsize */
398 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
399 { 1, 100, 1, 0 }, /* stop_length */
402 VikLayerParam trw_layer_params[] = {
403 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
404 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
406 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
407 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
408 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
409 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
410 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
412 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
413 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
415 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
416 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
417 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
418 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
420 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
421 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
422 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
423 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
424 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
425 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
426 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
427 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
429 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
430 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
431 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
432 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
435 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_TDSF, 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 };
438 *** 1) Add to trw_layer_params and enumeration
439 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
442 /****** END PARAMETERS ******/
444 static VikTrwLayer* trw_layer_new ( gint drawmode );
445 /* Layer Interface function definitions */
446 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
447 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
448 static void trw_layer_free ( VikTrwLayer *trwlayer );
449 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
450 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
451 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
452 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
453 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
454 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
455 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
456 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
457 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
458 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
459 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
460 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
461 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
462 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
463 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
464 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
465 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
466 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
467 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
468 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
469 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
470 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
471 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
472 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
473 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
474 /* End Layer Interface function definitions */
476 VikLayerInterface vik_trw_layer_interface = {
482 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
486 params_groups, /* params_groups */
487 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
491 (VikLayerFuncCreate) trw_layer_create,
492 (VikLayerFuncRealize) trw_layer_realize,
493 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
494 (VikLayerFuncFree) trw_layer_free,
496 (VikLayerFuncProperties) NULL,
497 (VikLayerFuncDraw) trw_layer_draw,
498 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
500 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
501 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
503 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
504 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
506 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
507 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
508 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
509 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
510 (VikLayerFuncLayerSelected) trw_layer_selected,
512 (VikLayerFuncMarshall) trw_layer_marshall,
513 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
515 (VikLayerFuncSetParam) trw_layer_set_param,
516 (VikLayerFuncGetParam) trw_layer_get_param,
518 (VikLayerFuncReadFileData) a_gpspoint_read_file,
519 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
521 (VikLayerFuncDeleteItem) trw_layer_del_item,
522 (VikLayerFuncCutItem) trw_layer_cut_item,
523 (VikLayerFuncCopyItem) trw_layer_copy_item,
524 (VikLayerFuncPasteItem) trw_layer_paste_item,
525 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
527 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
529 (VikLayerFuncSelectClick) trw_layer_select_click,
530 (VikLayerFuncSelectMove) trw_layer_select_move,
531 (VikLayerFuncSelectRelease) trw_layer_select_release,
532 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
541 GType vik_trw_layer_get_type ()
543 static GType vtl_type = 0;
547 static const GTypeInfo vtl_info =
549 sizeof (VikTrwLayerClass),
550 NULL, /* base_init */
551 NULL, /* base_finalize */
552 NULL, /* class init */
553 NULL, /* class_finalize */
554 NULL, /* class_data */
555 sizeof (VikTrwLayer),
557 NULL /* instance init */
559 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
565 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
567 static gpointer pass_along[6];
573 pass_along[1] = NULL;
574 pass_along[2] = GINT_TO_POINTER (subtype);
575 pass_along[3] = sublayer;
576 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
577 pass_along[5] = NULL;
579 trw_layer_delete_item ( pass_along );
582 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
584 static gpointer pass_along[6];
590 pass_along[1] = NULL;
591 pass_along[2] = GINT_TO_POINTER (subtype);
592 pass_along[3] = sublayer;
593 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
594 pass_along[5] = NULL;
596 trw_layer_copy_item_cb(pass_along);
597 trw_layer_cut_item_cb(pass_along);
600 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
602 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
603 gint subtype = GPOINTER_TO_INT (pass_along[2]);
604 gpointer * sublayer = pass_along[3];
608 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
612 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
613 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
614 if ( wp && wp->name )
617 name = NULL; // Broken :(
620 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
621 if ( trk && trk->name )
624 name = NULL; // Broken :(
627 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
628 subtype, len, name, data);
632 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
634 trw_layer_copy_item_cb(pass_along);
635 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
636 trw_layer_delete_item(pass_along);
639 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
650 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
652 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
653 // 'Simple' memory copy of byte array from the marshalling above
654 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
655 fi = g_malloc ( *len );
657 memcpy(fi->data, id, il);
660 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
661 // less magic than before...
662 *len = sizeof(FlatItem) + 1 + il;
663 fi = g_malloc ( *len );
665 memcpy(fi->data, id, il);
669 *item = (guint8 *)fi;
672 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
674 FlatItem *fi = (FlatItem *) item;
676 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
681 w = vik_waypoint_unmarshall(fi->data, fi->len);
682 // When copying - we'll create a new name based on the original
683 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
684 vik_trw_layer_add_waypoint ( vtl, name, w );
685 waypoint_convert (NULL, w, &vtl->coord_mode);
687 // Consider if redraw necessary for the new item
688 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
689 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
692 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
697 t = vik_track_unmarshall(fi->data, fi->len);
698 // When copying - we'll create a new name based on the original
699 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
700 vik_trw_layer_add_track ( vtl, name, t );
701 track_convert (name, t, &vtl->coord_mode);
703 // Consider if redraw necessary for the new item
704 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
705 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
711 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
718 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
722 case PARAM_TV: vtl->tracks_visible = data.b; break;
723 case PARAM_WV: vtl->waypoints_visible = data.b; break;
724 case PARAM_DM: vtl->drawmode = data.u; break;
725 case PARAM_DP: vtl->drawpoints = data.b; break;
726 case PARAM_DE: vtl->drawelevation = data.b; break;
727 case PARAM_DS: vtl->drawstops = data.b; break;
728 case PARAM_DL: vtl->drawlines = data.b; break;
729 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
730 vtl->stop_length = data.u;
732 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
733 vtl->elevation_factor = data.u;
735 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
737 vtl->line_thickness = data.u;
738 trw_layer_new_track_gcs ( vtl, vp );
741 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
743 vtl->bg_line_thickness = data.u;
744 trw_layer_new_track_gcs ( vtl, vp );
747 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
748 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
749 case PARAM_DLA: vtl->drawlabels = data.b; break;
750 case PARAM_DI: vtl->drawimages = data.b; break;
751 case PARAM_IS: if ( data.u != vtl->image_size )
753 vtl->image_size = data.u;
754 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
755 g_queue_free ( vtl->image_cache );
756 vtl->image_cache = g_queue_new ();
759 case PARAM_IA: vtl->image_alpha = data.u; break;
760 case PARAM_ICS: vtl->image_cache_size = data.u;
761 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
762 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
764 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
765 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
766 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
767 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
768 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
769 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
770 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
775 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
777 VikLayerParamData rv;
780 case PARAM_TV: rv.b = vtl->tracks_visible; break;
781 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
782 case PARAM_DM: rv.u = vtl->drawmode; break;
783 case PARAM_DP: rv.b = vtl->drawpoints; break;
784 case PARAM_DE: rv.b = vtl->drawelevation; break;
785 case PARAM_EF: rv.u = vtl->elevation_factor; break;
786 case PARAM_DS: rv.b = vtl->drawstops; break;
787 case PARAM_SL: rv.u = vtl->stop_length; break;
788 case PARAM_DL: rv.b = vtl->drawlines; break;
789 case PARAM_LT: rv.u = vtl->line_thickness; break;
790 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
791 case PARAM_DLA: rv.b = vtl->drawlabels; break;
792 case PARAM_DI: rv.b = vtl->drawimages; break;
793 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
794 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
795 case PARAM_IS: rv.u = vtl->image_size; break;
796 case PARAM_IA: rv.u = vtl->image_alpha; break;
797 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
798 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
799 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
800 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
801 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
802 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
803 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
804 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
809 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
820 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
821 a_gpx_write_file(vtl, f);
822 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
825 g_file_get_contents(tmpname, &dd, &dl, NULL);
826 *len = sizeof(pl) + pl + dl;
827 *data = g_malloc(*len);
828 memcpy(*data, &pl, sizeof(pl));
829 memcpy(*data + sizeof(pl), pd, pl);
830 memcpy(*data + sizeof(pl) + pl, dd, dl);
839 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
841 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
847 memcpy(&pl, data, sizeof(pl));
849 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
852 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
853 g_critical("couldn't open temp file");
856 fwrite(data, len - pl - sizeof(pl), 1, f);
858 a_gpx_read_file(rv, f);
866 static GList * str_array_to_glist(gchar* data[])
870 for (p = (gpointer)data; *p; p++)
871 gl = g_list_prepend(gl, *p);
872 return(g_list_reverse(gl));
875 // Keep interesting hash function at least visible
877 static guint strcase_hash(gconstpointer v)
879 // 31 bit hash function
882 gchar s[128]; // malloc is too slow for reading big files
885 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
886 p[i] = toupper(t[i]);
892 for (p += 1; *p != '\0'; p++)
893 h = (h << 5) - h + *p;
900 static VikTrwLayer* trw_layer_new ( gint drawmode )
902 if (trw_layer_params[PARAM_DM].widget_data == NULL)
903 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
904 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
905 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
907 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
908 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
910 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
911 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
913 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
914 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
915 // and with normal PC processing capabilities - it has negligibile performance impact
916 // This also minimized the amount of rework - as the management of the hash tables already exists.
918 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
919 // we have to maintain a uniqueness (as before when multiple names where not allowed),
920 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
922 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
923 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
924 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
925 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
927 /* TODO: constants at top */
928 rv->waypoints_visible = rv->tracks_visible = TRUE;
929 rv->drawmode = drawmode;
930 rv->drawpoints = TRUE;
931 rv->drawstops = FALSE;
932 rv->drawelevation = FALSE;
933 rv->elevation_factor = 30;
934 rv->stop_length = 60;
935 rv->drawlines = TRUE;
936 rv->wplabellayout = NULL;
937 rv->wp_right_click_menu = NULL;
938 rv->track_right_click_menu = NULL;
939 rv->waypoint_gc = NULL;
940 rv->waypoint_text_gc = NULL;
941 rv->waypoint_bg_gc = NULL;
943 rv->track_draw_speed_factor = 30.0;
944 rv->line_thickness = 1;
945 rv->bg_line_thickness = 0;
946 rv->current_wp = NULL;
947 rv->current_wp_id = NULL;
948 rv->current_track = NULL;
949 rv->current_tpl = NULL;
950 rv->current_tp_track = NULL;
951 rv->current_tp_id = NULL;
952 rv->moving_tp = FALSE;
953 rv->moving_wp = FALSE;
955 rv->ct_sync_done = TRUE;
957 rv->route_finder_started = FALSE;
958 rv->route_finder_check_added_track = FALSE;
959 rv->route_finder_current_track = NULL;
960 rv->route_finder_append = FALSE;
962 rv->waypoint_rightclick = FALSE;
964 rv->last_tp_track = NULL;
966 rv->image_cache = g_queue_new();
968 rv->image_alpha = 255;
969 rv->image_cache_size = 300;
970 rv->drawimages = TRUE;
971 rv->drawlabels = TRUE;
976 static void trw_layer_free ( VikTrwLayer *trwlayer )
978 g_hash_table_destroy(trwlayer->waypoints);
979 g_hash_table_destroy(trwlayer->tracks);
981 /* ODC: replace with GArray */
982 trw_layer_free_track_gcs ( trwlayer );
984 if ( trwlayer->wp_right_click_menu )
985 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
987 if ( trwlayer->track_right_click_menu )
988 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
990 if ( trwlayer->wplabellayout != NULL)
991 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
993 if ( trwlayer->waypoint_gc != NULL )
994 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
996 if ( trwlayer->waypoint_text_gc != NULL )
997 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
999 if ( trwlayer->waypoint_bg_gc != NULL )
1000 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1002 if ( trwlayer->waypoint_font != NULL )
1003 gdk_font_unref ( trwlayer->waypoint_font );
1005 if ( trwlayer->tpwin != NULL )
1006 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1008 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1009 g_queue_free ( trwlayer->image_cache );
1012 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
1015 dp->xmpp = vik_viewport_get_xmpp ( vp );
1016 dp->ympp = vik_viewport_get_ympp ( vp );
1017 dp->width = vik_viewport_get_width ( vp );
1018 dp->height = vik_viewport_get_height ( vp );
1019 dp->center = vik_viewport_get_center ( vp );
1020 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1021 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1026 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1027 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1028 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1030 dp->ce1 = dp->center->east_west-w2;
1031 dp->ce2 = dp->center->east_west+w2;
1032 dp->cn1 = dp->center->north_south-h2;
1033 dp->cn2 = dp->center->north_south+h2;
1034 } else if ( dp->lat_lon ) {
1035 VikCoord upperleft, bottomright;
1036 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1037 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1038 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1039 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1040 dp->ce1 = upperleft.east_west;
1041 dp->ce2 = bottomright.east_west;
1042 dp->cn1 = bottomright.north_south;
1043 dp->cn2 = upperleft.north_south;
1046 dp->track_gc_iter = 0;
1050 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1051 * Here a simple traffic like light colour system is used:
1052 * . slow points are red
1053 * . average is yellow
1054 * . fast points are green
1056 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1059 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1060 if ( average_speed > 0 ) {
1061 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1062 if ( rv < low_speed )
1063 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1064 else if ( rv > high_speed )
1065 return VIK_TRW_LAYER_TRACK_GC_FAST;
1067 return VIK_TRW_LAYER_TRACK_GC_AVER;
1070 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1073 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1075 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1076 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1077 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1078 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1081 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1083 /* TODO: this function is a mess, get rid of any redundancy */
1084 GList *list = track->trackpoints;
1086 gboolean useoldvals = TRUE;
1088 gboolean drawpoints;
1090 gboolean drawelevation;
1091 gdouble min_alt, max_alt, alt_diff = 0;
1093 const guint8 tp_size_reg = 2;
1094 const guint8 tp_size_cur = 4;
1097 if ( dp->vtl->drawelevation )
1099 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1100 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1101 alt_diff = max_alt - min_alt;
1104 if ( ! track->visible )
1107 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1108 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1109 trw_layer_draw_track ( name, track, dp, TRUE );
1111 if ( drawing_white_background )
1112 drawpoints = drawstops = FALSE;
1114 drawpoints = dp->vtl->drawpoints;
1115 drawstops = dp->vtl->drawstops;
1118 gboolean drawing_highlight = FALSE;
1119 /* Current track - used for creation */
1120 if ( track == dp->vtl->current_track )
1121 main_gc = dp->vtl->current_track_gc;
1123 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1124 /* Draw all tracks of the layer in special colour */
1125 /* if track is member of selected layer or is the current selected track
1126 then draw in the highlight colour.
1127 NB this supercedes the drawmode */
1128 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1129 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1130 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1131 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1132 drawing_highlight = TRUE;
1135 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1136 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1138 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
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);
1150 int x, y, oldx, oldy;
1151 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1153 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1155 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1157 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1159 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1160 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1166 gdouble average_speed = 0.0;
1167 gdouble low_speed = 0.0;
1168 gdouble high_speed = 0.0;
1169 // If necessary calculate these values - which is done only once per track redraw
1170 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1171 // the percentage factor away from the average speed determines transistions between the levels
1172 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1173 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1174 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1177 while ((list = g_list_next(list)))
1179 tp = VIK_TRACKPOINT(list->data);
1180 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1182 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1183 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1184 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1185 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1186 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1188 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1191 * If points are the same in display coordinates, don't draw.
1193 if ( useoldvals && x == oldx && y == oldy )
1195 // Still need to process points to ensure 'stops' are drawn if required
1196 if ( drawstops && drawpoints && ! drawing_white_background && list->next &&
1197 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1198 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 );
1203 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1204 if ( drawpoints || dp->vtl->drawlines ) {
1205 // setup main_gc for both point and line drawing
1206 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1207 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1208 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1212 if ( drawpoints && ! drawing_white_background )
1217 * The concept of drawing stops is that a trackpoint
1218 * that is if the next trackpoint has a timestamp far into
1219 * the future, we draw a circle of 6x trackpoint size,
1220 * instead of a rectangle of 2x trackpoint size.
1221 * This is drawn first so the trackpoint will be drawn on top
1224 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1225 /* Stop point. Draw 6x circle. */
1226 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 );
1228 /* Regular point - draw 2x square. */
1229 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1232 /* Final point - draw 4x circle. */
1233 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 );
1236 if ((!tp->newsegment) && (dp->vtl->drawlines))
1239 /* UTM only: zone check */
1240 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1241 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1244 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1246 if ( drawing_white_background ) {
1247 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1251 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1252 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1254 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1259 tmp[1].y = oldy-FIXALTITUDE(list->data);
1261 tmp[2].y = y-FIXALTITUDE(list->next->data);
1266 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1267 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1269 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1270 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1272 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1283 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1285 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1286 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1288 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1290 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1291 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1292 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1296 * If points are the same in display coordinates, don't draw.
1298 if ( x != oldx || y != oldy )
1300 if ( drawing_white_background )
1301 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1303 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1309 * If points are the same in display coordinates, don't draw.
1311 if ( x != oldx && y != oldy )
1313 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1314 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1322 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1323 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
1324 dp->track_gc_iter = 0;
1327 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1328 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1330 trw_layer_draw_track ( name, track, dp, FALSE );
1333 static void cached_pixbuf_free ( CachedPixbuf *cp )
1335 g_object_unref ( G_OBJECT(cp->pixbuf) );
1336 g_free ( cp->image );
1339 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1341 return strcmp ( cp->image, name );
1344 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1347 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1348 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1349 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1352 GdkPixbuf *sym = NULL;
1353 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1355 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1357 if ( wp->image && dp->vtl->drawimages )
1359 GdkPixbuf *pixbuf = NULL;
1362 if ( dp->vtl->image_alpha == 0)
1365 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1367 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1370 gchar *image = wp->image;
1371 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1372 if ( ! regularthumb )
1374 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1375 image = "\x12\x00"; /* this shouldn't occur naturally. */
1379 CachedPixbuf *cp = NULL;
1380 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1381 if ( dp->vtl->image_size == 128 )
1382 cp->pixbuf = regularthumb;
1385 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1386 g_assert ( cp->pixbuf );
1387 g_object_unref ( G_OBJECT(regularthumb) );
1389 cp->image = g_strdup ( image );
1391 /* needed so 'click picture' tool knows how big the pic is; we don't
1392 * store it in cp because they may have been freed already. */
1393 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1394 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1396 g_queue_push_head ( dp->vtl->image_cache, cp );
1397 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1398 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1400 pixbuf = cp->pixbuf;
1404 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1410 w = gdk_pixbuf_get_width ( pixbuf );
1411 h = gdk_pixbuf_get_height ( pixbuf );
1413 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1415 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1416 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1417 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1418 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1419 // Highlighted - so draw a little border around the chosen one
1420 // single line seems a little weak so draw 2 of them
1421 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1422 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1423 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1424 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1427 if ( dp->vtl->image_alpha == 255 )
1428 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1430 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1432 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1436 /* DRAW ACTUAL DOT */
1437 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1438 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 );
1440 else if ( wp == dp->vtl->current_wp ) {
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), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
1443 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;
1444 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;
1445 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 );
1446 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 );
1450 switch ( dp->vtl->wp_symbol ) {
1451 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;
1452 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;
1453 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;
1454 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 );
1455 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;
1459 if ( dp->vtl->drawlabels )
1461 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1462 gint label_x, label_y;
1464 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1465 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1466 label_x = x - width/2;
1468 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1470 label_y = y - dp->vtl->wp_size - height - 2;
1472 /* if highlight mode on, then draw background text in highlight colour */
1473 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1474 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1475 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1476 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1477 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1479 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1482 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1484 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1489 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1491 static struct DrawingParams dp;
1492 g_assert ( l != NULL );
1494 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1497 if ( l->tracks_visible )
1498 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1500 if (l->waypoints_visible)
1501 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1504 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1507 if ( vtl->track_bg_gc )
1509 g_object_unref ( vtl->track_bg_gc );
1510 vtl->track_bg_gc = NULL;
1512 if ( vtl->current_track_gc )
1514 g_object_unref ( vtl->current_track_gc );
1515 vtl->current_track_gc = NULL;
1518 if ( ! vtl->track_gc )
1520 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1521 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1522 g_array_free ( vtl->track_gc, TRUE );
1523 vtl->track_gc = NULL;
1526 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1528 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1529 gint width = vtl->line_thickness;
1531 if ( vtl->track_gc )
1532 trw_layer_free_track_gcs ( vtl );
1534 if ( vtl->track_bg_gc )
1535 g_object_unref ( vtl->track_bg_gc );
1536 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1538 if ( vtl->current_track_gc )
1539 g_object_unref ( vtl->current_track_gc );
1540 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1541 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1543 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1545 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1547 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1548 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1549 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1550 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1551 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1552 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1553 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1554 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1555 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1556 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1558 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1560 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1562 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1563 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1564 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1566 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1569 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1571 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1572 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1574 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1575 /* early exit, as the rest is GUI related */
1579 PangoFontDescription *pfd;
1580 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1581 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1582 pango_layout_set_font_description (rv->wplabellayout, pfd);
1583 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1584 pango_font_description_free (pfd);
1586 trw_layer_new_track_gcs ( rv, vp );
1588 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1589 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1590 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1591 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1593 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1595 rv->has_verified_thumbnails = FALSE;
1596 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1598 rv->wp_draw_symbols = TRUE;
1600 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1602 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1607 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1609 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1611 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1612 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1614 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1617 *new_iter = *((GtkTreeIter *) pass_along[1]);
1618 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1620 if ( ! track->visible )
1621 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1624 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1626 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1627 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1628 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1630 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), NULL, TRUE, TRUE );
1633 *new_iter = *((GtkTreeIter *) pass_along[1]);
1634 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1636 if ( ! wp->visible )
1637 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1641 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1644 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1646 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1647 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1649 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1651 if ( ! vtl->tracks_visible )
1652 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1654 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1656 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1657 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1659 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1662 if ( ! vtl->waypoints_visible )
1663 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1665 pass_along[0] = &(vtl->waypoints_iter);
1666 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1668 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1672 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1676 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1677 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1678 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1680 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1682 return (t->visible ^= 1);
1686 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1688 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1690 return (t->visible ^= 1);
1699 * Return a property about tracks for this layer
1701 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1703 return vtl->line_thickness;
1706 // Structure to hold multiple track information for a layer
1715 * Build up layer multiple track information via updating the tooltip_tracks structure
1717 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1719 tt->length = tt->length + vik_track_get_length (tr);
1721 // Ensure times are available
1722 if ( tr->trackpoints &&
1723 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1724 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1727 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1728 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1730 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1731 // Hence initialize to the first 'proper' value
1732 if ( tt->start_time == 0 )
1733 tt->start_time = t1;
1734 if ( tt->end_time == 0 )
1737 // Update find the earliest / last times
1738 if ( t1 < tt->start_time )
1739 tt->start_time = t1;
1740 if ( t2 > tt->end_time )
1743 // Keep track of total time
1744 // there maybe gaps within a track (eg segments)
1745 // but this should be generally good enough for a simple indicator
1746 tt->duration = tt->duration + (int)(t2-t1);
1751 * Generate tooltip text for the layer.
1752 * This is relatively complicated as it considers information for
1753 * no tracks, a single track or multiple tracks
1754 * (which may or may not have timing information)
1756 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1767 static gchar tmp_buf[128];
1770 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1772 // Safety check - I think these should always be valid
1773 if ( vtl->tracks && vtl->waypoints ) {
1774 tooltip_tracks tt = { 0.0, 0, 0 };
1775 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1777 GDate* gdate_start = g_date_new ();
1778 g_date_set_time_t (gdate_start, tt.start_time);
1780 GDate* gdate_end = g_date_new ();
1781 g_date_set_time_t (gdate_end, tt.end_time);
1783 if ( g_date_compare (gdate_start, gdate_end) ) {
1784 // Dates differ so print range on separate line
1785 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1786 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1787 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1790 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1791 if ( tt.start_time != 0 )
1792 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1796 if ( tt.length > 0.0 ) {
1797 gdouble len_in_units;
1799 // Setup info dependent on distance units
1800 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1801 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1802 len_in_units = VIK_METERS_TO_MILES(tt.length);
1805 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1806 len_in_units = tt.length/1000.0;
1809 // Timing information if available
1811 if ( tt.duration > 0 ) {
1812 g_snprintf (tbuf1, sizeof(tbuf1),
1813 _(" in %d:%02d hrs:mins"),
1814 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1816 g_snprintf (tbuf2, sizeof(tbuf2),
1817 _("\n%sTotal Length %.1f %s%s"),
1818 tbuf3, len_in_units, tbuf4, tbuf1);
1821 // Put together all the elements to form compact tooltip text
1822 g_snprintf (tmp_buf, sizeof(tmp_buf),
1823 _("Tracks: %d - Waypoints: %d%s"),
1824 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1826 g_date_free (gdate_start);
1827 g_date_free (gdate_end);
1834 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1838 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1839 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1840 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1842 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1844 // Could be a better way of handling strings - but this works...
1845 gchar time_buf1[20];
1846 gchar time_buf2[20];
1847 time_buf1[0] = '\0';
1848 time_buf2[0] = '\0';
1849 static gchar tmp_buf[100];
1850 // Compact info: Short date eg (11/20/99), duration and length
1851 // Hopefully these are the things that are most useful and so promoted into the tooltip
1852 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1853 // %x The preferred date representation for the current locale without the time.
1854 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1855 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1856 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1858 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1861 // Get length and consider the appropriate distance units
1862 gdouble tr_len = vik_track_get_length(tr);
1863 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1864 switch (dist_units) {
1865 case VIK_UNITS_DISTANCE_KILOMETRES:
1866 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1868 case VIK_UNITS_DISTANCE_MILES:
1869 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1878 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1880 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1881 // NB It's OK to return NULL
1892 * Function to show basic track point information on the statusbar
1894 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1897 switch (a_vik_get_units_height ()) {
1898 case VIK_UNITS_HEIGHT_FEET:
1899 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1902 //VIK_UNITS_HEIGHT_METRES:
1903 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1908 if ( trkpt->has_timestamp ) {
1909 // Compact date time format
1910 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1914 // Position is put later on, as this bit may not be seen if the display is not big enough,
1915 // one can easily use the current pointer position to see this if needed
1916 gchar *lat = NULL, *lon = NULL;
1917 static struct LatLon ll;
1918 vik_coord_to_latlon (&(trkpt->coord), &ll);
1919 a_coords_latlon_to_string ( &ll, &lat, &lon );
1922 // Again is put later on, as this bit may not be seen if the display is not big enough
1923 // trackname can be seen from the treeview (when enabled)
1924 // Also name could be very long to not leave room for anything else
1927 if ( vtl->current_tp_track ) {
1928 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
1931 // Combine parts to make overall message
1932 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
1933 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1940 * Function to show basic waypoint information on the statusbar
1942 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
1945 switch (a_vik_get_units_height ()) {
1946 case VIK_UNITS_HEIGHT_FEET:
1947 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
1950 //VIK_UNITS_HEIGHT_METRES:
1951 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
1955 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
1956 // one can easily use the current pointer position to see this if needed
1957 gchar *lat = NULL, *lon = NULL;
1958 static struct LatLon ll;
1959 vik_coord_to_latlon (&(wpt->coord), &ll);
1960 a_coords_latlon_to_string ( &ll, &lat, &lon );
1962 // Combine parts to make overall message
1965 // Add comment if available
1966 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
1968 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
1969 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1976 * General layer selection function, find out which bit is selected and take appropriate action
1978 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1981 l->current_wp = NULL;
1982 l->current_wp_id = NULL;
1983 trw_layer_cancel_current_tp ( l, FALSE );
1986 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
1990 case VIK_TREEVIEW_TYPE_LAYER:
1992 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1993 /* Mark for redraw */
1998 case VIK_TREEVIEW_TYPE_SUBLAYER:
2002 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2004 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2005 /* Mark for redraw */
2009 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2011 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2012 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
2013 /* Mark for redraw */
2017 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2019 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2020 /* Mark for redraw */
2024 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2026 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2028 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
2029 // Show some waypoint info
2030 set_statusbar_msg_info_wpt ( l, wpt );
2031 /* Mark for redraw */
2038 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2047 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2052 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2057 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2059 return l->waypoints;
2063 * ATM use a case sensitive find
2064 * Finds the first one
2066 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2068 if ( wp && wp->name )
2069 if ( ! strcmp ( wp->name, name ) )
2075 * Get waypoint by name - not guaranteed to be unique
2076 * Finds the first one
2078 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2080 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2084 * ATM use a case sensitive find
2085 * Finds the first one
2087 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2089 if ( trk && trk->name )
2090 if ( ! strcmp ( trk->name, name ) )
2096 * Get track by name - not guaranteed to be unique
2097 * Finds the first one
2099 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2101 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2104 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2106 static VikCoord fixme;
2107 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2108 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2109 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2110 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2111 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2112 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2113 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2114 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2115 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2118 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
2121 static VikCoord fixme;
2125 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2126 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2127 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2128 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2129 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2130 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2131 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2132 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2133 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2138 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2140 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2141 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2143 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2144 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2145 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2146 maxmin[0].lat = wpt_maxmin[0].lat;
2149 maxmin[0].lat = trk_maxmin[0].lat;
2151 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2152 maxmin[0].lon = wpt_maxmin[0].lon;
2155 maxmin[0].lon = trk_maxmin[0].lon;
2157 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2158 maxmin[1].lat = wpt_maxmin[1].lat;
2161 maxmin[1].lat = trk_maxmin[1].lat;
2163 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2164 maxmin[1].lon = wpt_maxmin[1].lon;
2167 maxmin[1].lon = trk_maxmin[1].lon;
2171 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2173 /* 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... */
2174 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2175 trw_layer_find_maxmin (vtl, maxmin);
2176 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2180 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2181 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2186 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2189 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2190 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2192 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2195 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2197 /* First set the center [in case previously viewing from elsewhere] */
2198 /* Then loop through zoom levels until provided positions are in view */
2199 /* This method is not particularly fast - but should work well enough */
2200 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2202 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2203 vik_viewport_set_center_coord ( vvp, &coord );
2205 /* Convert into definite 'smallest' and 'largest' positions */
2206 struct LatLon minmin;
2207 if ( maxmin[0].lat < maxmin[1].lat )
2208 minmin.lat = maxmin[0].lat;
2210 minmin.lat = maxmin[1].lat;
2212 struct LatLon maxmax;
2213 if ( maxmin[0].lon > maxmin[1].lon )
2214 maxmax.lon = maxmin[0].lon;
2216 maxmax.lon = maxmin[1].lon;
2218 /* Never zoom in too far - generally not that useful, as too close ! */
2219 /* Always recalculate the 'best' zoom level */
2221 vik_viewport_set_zoom ( vvp, zoom );
2223 gdouble min_lat, max_lat, min_lon, max_lon;
2224 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2225 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2226 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2227 /* NB I think the logic used in this test to determine if the bounds is within view
2228 fails if track goes across 180 degrees longitude.
2229 Hopefully that situation is not too common...
2230 Mind you viking doesn't really do edge locations to well anyway */
2231 if ( min_lat < minmin.lat &&
2232 max_lat > minmin.lat &&
2233 min_lon < maxmax.lon &&
2234 max_lon > maxmax.lon )
2235 /* Found within zoom level */
2240 vik_viewport_set_zoom ( vvp, zoom );
2244 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2246 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2247 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2248 trw_layer_find_maxmin (vtl, maxmin);
2249 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2252 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2257 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2259 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])) ) ) {
2260 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2263 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2266 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2268 GtkWidget *file_selector;
2270 gboolean failed = FALSE;
2271 file_selector = gtk_file_chooser_dialog_new (title,
2273 GTK_FILE_CHOOSER_ACTION_SAVE,
2274 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2275 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2277 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2279 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2281 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2282 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2284 gtk_widget_hide ( file_selector );
2285 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk );
2290 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2292 gtk_widget_hide ( file_selector );
2293 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk );
2298 gtk_widget_destroy ( file_selector );
2300 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2303 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2305 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2308 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2310 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2313 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2315 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2316 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2317 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2318 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2320 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2322 g_free ( auto_save_name );
2325 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2327 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2328 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2329 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2330 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2332 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2334 g_free ( auto_save_name );
2338 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2341 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2343 gchar *name_used = NULL;
2346 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2347 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL);
2349 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2353 gchar *quoted_file = g_shell_quote ( name_used );
2354 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2355 g_free ( quoted_file );
2356 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2358 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2359 g_error_free ( err );
2363 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2364 //g_remove ( name_used );
2365 // Perhaps should be deleted when the program ends?
2366 // For now leave it to the user to delete it / use system temp cleanup methods.
2367 g_free ( name_used );
2371 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2373 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2376 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2378 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2381 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2383 gpointer layer_and_vlp[2];
2384 layer_and_vlp[0] = pass_along[0];
2385 layer_and_vlp[1] = pass_along[1];
2386 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2388 if ( !trk || !trk->name )
2391 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2392 gchar *auto_save_name = g_strdup ( trk->name );
2393 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2394 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2396 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2398 g_free ( auto_save_name );
2402 VikWaypoint *wp; // input
2403 gpointer uuid; // output
2406 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2408 wpu_udata *user_data = udata;
2409 if ( wp == user_data->wp ) {
2410 user_data->uuid = id;
2416 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2418 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2419 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2420 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2422 GTK_RESPONSE_REJECT,
2424 GTK_RESPONSE_ACCEPT,
2427 GtkWidget *label, *entry;
2428 label = gtk_label_new(_("Waypoint Name:"));
2429 entry = gtk_entry_new();
2431 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2432 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2433 gtk_widget_show_all ( label );
2434 gtk_widget_show_all ( entry );
2436 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2438 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2440 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2441 // Find *first* wp with the given name
2442 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2445 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2448 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2449 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2451 // Find and select on the side panel
2456 // Hmmm, want key of it
2457 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2459 if ( wpf && udata.uuid ) {
2460 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2461 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2470 gtk_widget_destroy ( dia );
2473 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2475 gchar *default_name = highest_wp_number_get(vtl);
2476 VikWaypoint *wp = vik_waypoint_new();
2477 gchar *returned_name;
2479 wp->coord = *def_coord;
2481 // Attempt to auto set height if DEM data is available
2482 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2483 if ( elev != VIK_DEM_INVALID_ELEVATION )
2484 wp->altitude = (gdouble)elev;
2486 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2488 if ( returned_name )
2491 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2492 g_free (default_name);
2493 g_free (returned_name);
2496 g_free (default_name);
2497 vik_waypoint_free(wp);
2501 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2504 struct LatLon one_ll, two_ll;
2505 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2507 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2508 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2509 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2510 VikViewport *vvp = vik_window_viewport(vw);
2511 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2512 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2513 vik_coord_to_latlon(&one, &one_ll);
2514 vik_coord_to_latlon(&two, &two_ll);
2515 if (one_ll.lat > two_ll.lat) {
2516 maxmin[0].lat = one_ll.lat;
2517 maxmin[1].lat = two_ll.lat;
2520 maxmin[0].lat = two_ll.lat;
2521 maxmin[1].lat = one_ll.lat;
2523 if (one_ll.lon > two_ll.lon) {
2524 maxmin[0].lon = one_ll.lon;
2525 maxmin[1].lon = two_ll.lon;
2528 maxmin[0].lon = two_ll.lon;
2529 maxmin[1].lon = one_ll.lon;
2531 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2534 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2536 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2537 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2538 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2540 trw_layer_find_maxmin (vtl, maxmin);
2541 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2544 #ifdef VIK_CONFIG_GEOTAG
2545 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2547 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2549 // Update directly - not changing the mtime
2550 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2553 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2555 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2558 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2562 * Use code in separate file for this feature as reasonably complex
2564 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2566 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2567 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2568 // Unset so can be reverified later if necessary
2569 vtl->has_verified_thumbnails = FALSE;
2571 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2577 static void trw_layer_geotagging ( gpointer lav[2] )
2579 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2580 // Unset so can be reverified later if necessary
2581 vtl->has_verified_thumbnails = FALSE;
2583 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2590 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2593 * Acquire into this TRW Layer straight from GPS Device
2595 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2597 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2598 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2599 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2600 VikViewport *vvp = vik_window_viewport(vw);
2602 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2603 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2607 * Acquire into this TRW Layer from Google Directions
2609 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2611 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2612 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2613 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2614 VikViewport *vvp = vik_window_viewport(vw);
2616 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2619 #ifdef VIK_CONFIG_OPENSTREETMAP
2621 * Acquire into this TRW Layer from OSM
2623 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2625 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2626 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2627 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2628 VikViewport *vvp = vik_window_viewport(vw);
2630 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2634 #ifdef VIK_CONFIG_GEOCACHES
2636 * Acquire into this TRW Layer from Geocaching.com
2638 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2640 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2641 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2642 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2643 VikViewport *vvp = vik_window_viewport(vw);
2645 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2649 #ifdef VIK_CONFIG_GEOTAG
2651 * Acquire into this TRW Layer from images
2653 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2655 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2656 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2657 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2658 VikViewport *vvp = vik_window_viewport(vw);
2660 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2661 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2663 // Reverify thumbnails as they may have changed
2664 vtl->has_verified_thumbnails = FALSE;
2665 trw_layer_verify_thumbnails ( vtl, NULL );
2669 static void trw_layer_new_wp ( gpointer lav[2] )
2671 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2672 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2673 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2674 instead return true if you want to update. */
2675 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 )
2676 vik_layers_panel_emit_update ( vlp );
2679 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2681 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2682 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2684 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2685 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2686 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2687 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2688 vik_layers_panel_emit_update ( vlp );
2692 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2694 /* NB do not care if wp is visible or not */
2695 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2698 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2700 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2701 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2703 /* Only 1 waypoint - jump straight to it */
2704 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2705 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2706 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2708 /* If at least 2 waypoints - find center and then zoom to fit */
2709 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2711 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2712 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2713 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2716 vik_layers_panel_emit_update ( vlp );
2719 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2721 static gpointer pass_along[2];
2723 GtkWidget *export_submenu;
2724 pass_along[0] = vtl;
2725 pass_along[1] = vlp;
2727 item = gtk_menu_item_new();
2728 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2729 gtk_widget_show ( item );
2731 /* Now with icons */
2732 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2733 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2734 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2735 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2736 gtk_widget_show ( item );
2738 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2739 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2740 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2741 gtk_widget_show ( item );
2743 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2744 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2745 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2746 gtk_widget_show ( item );
2748 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2749 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2750 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2751 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2752 gtk_widget_show ( item );
2754 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2755 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2756 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2757 gtk_widget_show ( item );
2759 export_submenu = gtk_menu_new ();
2760 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2761 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2762 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2763 gtk_widget_show ( item );
2764 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2766 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2767 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2768 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2769 gtk_widget_show ( item );
2771 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2772 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2773 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2774 gtk_widget_show ( item );
2776 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2777 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2778 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2779 gtk_widget_show ( item );
2781 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2782 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2783 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2784 gtk_widget_show ( item );
2786 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
2787 item = gtk_menu_item_new_with_mnemonic ( external1 );
2788 g_free ( external1 );
2789 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
2790 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2791 gtk_widget_show ( item );
2793 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
2794 item = gtk_menu_item_new_with_mnemonic ( external2 );
2795 g_free ( external2 );
2796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
2797 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2798 gtk_widget_show ( item );
2800 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2801 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2803 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2804 gtk_widget_show ( item );
2806 #ifdef VIK_CONFIG_GEONAMES
2807 GtkWidget *wikipedia_submenu = gtk_menu_new();
2808 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2809 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2810 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2811 gtk_widget_show(item);
2812 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2814 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2815 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2816 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2817 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2818 gtk_widget_show ( item );
2820 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2821 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2822 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2823 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2824 gtk_widget_show ( item );
2827 #ifdef VIK_CONFIG_GEOTAG
2828 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
2829 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
2830 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2831 gtk_widget_show ( item );
2834 GtkWidget *acquire_submenu = gtk_menu_new ();
2835 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2836 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2837 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2838 gtk_widget_show ( item );
2839 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2841 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2842 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2843 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2844 gtk_widget_show ( item );
2846 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2848 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2849 gtk_widget_show ( item );
2851 #ifdef VIK_CONFIG_OPENSTREETMAP
2852 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2853 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2854 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2855 gtk_widget_show ( item );
2858 #ifdef VIK_CONFIG_GEOCACHES
2859 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2860 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2861 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2862 gtk_widget_show ( item );
2865 #ifdef VIK_CONFIG_GEOTAG
2866 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
2867 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
2868 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2869 gtk_widget_show ( item );
2872 #ifdef VIK_CONFIG_OPENSTREETMAP
2873 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2874 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2875 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2876 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2877 gtk_widget_show ( item );
2880 GtkWidget *delete_submenu = gtk_menu_new ();
2881 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2882 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2883 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2884 gtk_widget_show ( item );
2885 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2887 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2888 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2889 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2890 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2891 gtk_widget_show ( item );
2893 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2896 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2897 gtk_widget_show ( item );
2899 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2902 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2903 gtk_widget_show ( item );
2905 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2906 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2908 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2909 gtk_widget_show ( item );
2911 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2912 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2914 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2915 gtk_widget_show ( item );
2918 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2919 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2921 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2922 gtk_widget_show ( item );
2926 // Fake Waypoint UUIDs vi simple increasing integer
2927 static guint wp_uuid = 0;
2929 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2933 vik_waypoint_set_name (wp, name);
2935 if ( VIK_LAYER(vtl)->realized )
2937 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2939 // Visibility column always needed for waypoints
2940 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2941 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2943 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2945 // Actual setting of visibility dependent on the waypoint
2946 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2948 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
2951 highest_wp_number_add_wp(vtl, name);
2952 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
2956 // Fake Track UUIDs vi simple increasing integer
2957 static guint tr_uuid = 0;
2959 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2963 vik_track_set_name (t, name);
2965 if ( VIK_LAYER(vtl)->realized )
2967 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2968 // Visibility column always needed for tracks
2969 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2970 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2972 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2974 // Actual setting of visibility dependent on the track
2975 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2977 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
2980 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
2984 /* to be called whenever a track has been deleted or may have been changed. */
2985 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
2987 if (vtl->current_tp_track == trk )
2988 trw_layer_cancel_current_tp ( vtl, FALSE );
2989 else if (vtl->last_tp_track == trk )
2990 trw_layer_cancel_last_tp ( vtl );
2993 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2996 gchar *newname = g_strdup(name);
2997 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2998 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2999 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3001 newname = new_newname;
3007 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3009 // No more uniqueness of name forced when loading from a file
3010 // This now makes this function a little redunant as we just flow the parameters through
3011 vik_trw_layer_add_waypoint ( vtl, name, wp );
3014 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3016 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3017 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3018 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3019 vik_track_free ( tr );
3020 vtl->route_finder_append = FALSE; /* this means we have added it */
3023 // No more uniqueness of name forced when loading from a file
3024 vik_trw_layer_add_track ( vtl, name, tr );
3026 if ( vtl->route_finder_check_added_track ) {
3027 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3028 vtl->route_finder_added_track = tr;
3033 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3035 *l = g_list_append(*l, id);
3039 * Move an item from one TRW layer to another TRW layer
3041 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3043 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3044 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3046 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, trk->name);
3048 VikTrack *trk2 = vik_track_copy ( trk );
3049 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3050 vik_trw_layer_delete_track ( vtl_src, trk );
3053 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3054 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3056 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, wp->name);
3058 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3059 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3060 trw_layer_delete_waypoint ( vtl_src, wp );
3064 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3066 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3067 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3069 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3070 GList *items = NULL;
3073 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3074 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3076 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3077 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3082 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3083 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3085 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3092 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3093 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3098 VikTrack *trk; // input
3099 gpointer uuid; // output
3102 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3104 trku_udata *user_data = udata;
3105 if ( trk == user_data->trk ) {
3106 user_data->uuid = id;
3112 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3114 gboolean was_visible = FALSE;
3116 if ( trk && trk->name ) {
3118 if ( trk == vtl->current_track ) {
3119 vtl->current_track = NULL;
3120 vtl->current_tp_track = NULL;
3121 vtl->current_tp_id = NULL;
3122 vtl->moving_tp = FALSE;
3125 was_visible = trk->visible;
3127 if ( trk == vtl->route_finder_current_track )
3128 vtl->route_finder_current_track = NULL;
3130 if ( trk == vtl->route_finder_added_track )
3131 vtl->route_finder_added_track = NULL;
3137 // Hmmm, want key of it
3138 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3140 if ( trkf && udata.uuid ) {
3141 /* could be current_tp, so we have to check */
3142 trw_layer_cancel_tps_of_track ( vtl, trk );
3144 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3147 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3148 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3149 g_hash_table_remove ( vtl->tracks, udata.uuid );
3156 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3158 gboolean was_visible = FALSE;
3160 if ( wp && wp->name ) {
3162 if ( wp == vtl->current_wp ) {
3163 vtl->current_wp = NULL;
3164 vtl->current_wp_id = NULL;
3165 vtl->moving_wp = FALSE;
3168 was_visible = wp->visible;
3174 // Hmmm, want key of it
3175 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3177 if ( wpf && udata.uuid ) {
3178 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3181 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3182 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3184 highest_wp_number_remove_wp(vtl, wp->name);
3185 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3194 // Only for temporary use by trw_layer_delete_waypoint_by_name
3195 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3197 wpu_udata *user_data = udata;
3198 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3199 user_data->uuid = id;
3206 * Delete a waypoint by the given name
3207 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3208 * as there be multiple waypoints with the same name
3210 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3213 // Fake a waypoint with the given name
3214 udata.wp = vik_waypoint_new ();
3215 vik_waypoint_set_name (udata.wp, name);
3216 // Currently only the name is used in this waypoint find function
3219 // Hmmm, want key of it
3220 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3222 vik_waypoint_free (udata.wp);
3224 if ( wpf && udata.uuid )
3225 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3231 VikTrack *trk; // input
3232 gpointer uuid; // output
3235 // Only for temporary use by trw_layer_delete_track_by_name
3236 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3238 tpu_udata *user_data = udata;
3239 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3240 user_data->uuid = id;
3247 * Delete a track by the given name
3248 * NOTE: ATM this will delete the first encountered Track with the specified name
3249 * as there be multiple track with the same name
3251 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3254 // Fake a track with the given name
3255 udata.trk = vik_track_new ();
3256 vik_track_set_name (udata.trk, name);
3257 // Currently only the name is used in this waypoint find function
3260 // Hmmm, want key of it
3261 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3263 vik_track_free (udata.trk);
3265 if ( trkf && udata.uuid )
3266 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3271 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3273 vik_treeview_item_delete (vt, it );
3276 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3279 vtl->current_track = NULL;
3280 vtl->route_finder_current_track = NULL;
3281 vtl->route_finder_added_track = NULL;
3282 if (vtl->current_tp_track)
3283 trw_layer_cancel_current_tp(vtl, FALSE);
3284 if (vtl->last_tp_track)
3285 trw_layer_cancel_last_tp ( vtl );
3287 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3288 g_hash_table_remove_all(vtl->tracks_iters);
3289 g_hash_table_remove_all(vtl->tracks);
3291 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3294 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3296 vtl->current_wp = NULL;
3297 vtl->current_wp_id = NULL;
3298 vtl->moving_wp = FALSE;
3300 highest_wp_number_reset(vtl);
3302 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3303 g_hash_table_remove_all(vtl->waypoints_iters);
3304 g_hash_table_remove_all(vtl->waypoints);
3306 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3309 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3311 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3312 // Get confirmation from the user
3313 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3314 _("Are you sure you want to delete all tracks in %s?"),
3315 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3316 vik_trw_layer_delete_all_tracks (vtl);
3319 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3321 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3322 // Get confirmation from the user
3323 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3324 _("Are you sure you want to delete all waypoints in %s?"),
3325 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3326 vik_trw_layer_delete_all_waypoints (vtl);
3329 static void trw_layer_delete_item ( gpointer pass_along[6] )
3331 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3332 gboolean was_visible = FALSE;
3333 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3335 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3336 if ( wp && wp->name ) {
3337 if ( GPOINTER_TO_INT ( pass_along[4]) )
3338 // Get confirmation from the user
3339 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3340 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3341 _("Are you sure you want to delete the waypoint \"%s\""),
3344 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3349 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3350 if ( trk && trk->name ) {
3351 if ( GPOINTER_TO_INT ( pass_along[4]) )
3352 // Get confirmation from the user
3353 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3354 _("Are you sure you want to delete the track \"%s\""),
3357 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3361 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3365 static void trw_layer_properties_item ( gpointer pass_along[7] )
3367 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3368 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3370 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3372 if ( wp && wp->name )
3374 gboolean updated = FALSE;
3375 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3377 if ( updated && VIK_LAYER(vtl)->visible )
3378 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3383 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3384 if ( tr && tr->name )
3386 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3388 pass_along[1], /* vlp */
3389 pass_along[5] ); /* vvp */
3395 Parameter 1 -> VikLayersPanel
3396 Parameter 2 -> VikLayer
3397 Parameter 3 -> VikViewport
3399 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3402 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3403 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3406 /* since vlp not set, vl & vvp should be valid instead! */
3408 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3409 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3414 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3416 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3417 if ( trps && trps->data )
3418 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3421 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3423 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
3424 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3425 if ( trps && *trps )
3427 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3429 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3430 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3431 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3432 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3433 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3437 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3439 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3440 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3442 vtl->current_track = track;
3443 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3445 if ( track->trackpoints )
3446 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3450 * extend a track using route finder
3452 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3454 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3455 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3456 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3458 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3459 vtl->route_finder_coord = last_coord;
3460 vtl->route_finder_current_track = track;
3461 vtl->route_finder_started = TRUE;
3463 if ( track->trackpoints )
3464 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3468 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3470 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3471 /* Also warn if overwrite old elevation data */
3472 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3474 vik_track_apply_dem_data ( track );
3477 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3479 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3482 trps = g_list_last(trps);
3483 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3486 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3488 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3491 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3494 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3496 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3499 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3502 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3504 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3507 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3511 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3513 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3515 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3516 if ( trps && *trps )
3518 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3519 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3520 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3521 if ( pass_along[1] )
3522 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3524 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3528 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3530 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3531 trw_layer_tpwin_init ( vtl );
3534 /*************************************
3535 * merge/split by time routines
3536 *************************************/
3538 /* called for each key in track hash table.
3539 * If the current track has the same time stamp type, add it to the result,
3540 * except the one pointed by "exclude".
3541 * set exclude to NULL if there is no exclude to check.
3542 * Note that the result is in reverse (for performance reasons).
3547 gboolean with_timestamps;
3549 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3551 twt_udata *user_data = udata;
3552 VikTrackpoint *p1, *p2;
3554 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3558 if (VIK_TRACK(value)->trackpoints) {
3559 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3560 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3562 if ( user_data->with_timestamps ) {
3563 if (!p1->has_timestamp || !p2->has_timestamp) {
3568 // Don't add tracks with timestamps when getting non timestamp tracks
3569 if (p1->has_timestamp || p2->has_timestamp) {
3575 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3578 /* called for each key in track hash table. if original track user_data[1] is close enough
3579 * to the passed one, add it to list in user_data[0]
3581 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3584 VikTrackpoint *p1, *p2;
3586 GList **nearby_tracks = ((gpointer *)user_data)[0];
3587 GList *orig_track = ((gpointer *)user_data)[1];
3588 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3591 * detect reasons for not merging, and return
3592 * if no reason is found not to merge, then do it.
3595 if (VIK_TRACK(value)->trackpoints == orig_track) {
3599 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3600 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3602 if (VIK_TRACK(value)->trackpoints) {
3603 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3604 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3606 if (!p1->has_timestamp || !p2->has_timestamp) {
3607 g_print("no timestamp\n");
3611 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3612 if (! (abs(t1 - p2->timestamp) < thr*60 ||
3614 abs(p1->timestamp - t2) < thr*60)
3621 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3624 /* comparison function used to sort tracks; a and b are hash table keys */
3625 /* Not actively used - can be restored if needed
3626 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3628 GHashTable *tracks = user_data;
3631 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3632 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3634 if (t1 < t2) return -1;
3635 if (t1 > t2) return 1;
3640 /* comparison function used to sort trackpoints */
3641 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3643 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3645 if (t1 < t2) return -1;
3646 if (t1 > t2) return 1;
3651 * comparison function which can be used to sort tracks or waypoints by name
3653 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3655 const gchar* namea = (const gchar*) a;
3656 const gchar* nameb = (const gchar*) b;
3657 if ( namea == NULL || nameb == NULL)
3660 // Same sort method as used in the vik_treeview_*_alphabetize functions
3661 return strcmp ( namea, nameb );
3665 * Attempt to merge selected track with other tracks specified by the user
3666 * Tracks to merge with must be of the same 'type' as the selected track -
3667 * either all with timestamps, or all without timestamps
3669 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3671 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3672 GList *other_tracks = NULL;
3673 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3675 if ( !track->trackpoints )
3679 udata.result = &other_tracks;
3680 udata.exclude = track->trackpoints;
3681 // Allow merging with 'similar' time type time tracks
3682 // i.e. either those times, or those without
3683 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3685 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3686 other_tracks = g_list_reverse(other_tracks);
3688 if ( !other_tracks ) {
3689 if ( udata.with_timestamps )
3690 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3692 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3696 // Sort alphabetically for user presentation
3697 // Convert into list of names for usage with dialog function
3698 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3699 GList *other_tracks_names = NULL;
3700 GList *iter = g_list_first ( other_tracks );
3702 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
3703 iter = g_list_next ( iter );
3706 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
3708 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3709 other_tracks_names, TRUE,
3710 _("Merge with..."), _("Select track to merge with"));
3711 g_list_free(other_tracks);
3712 g_list_free(other_tracks_names);
3717 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3718 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
3720 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3721 merge_track->trackpoints = NULL;
3722 vik_trw_layer_delete_track (vtl, merge_track);
3723 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3726 /* TODO: free data before free merge_list */
3727 for (l = merge_list; l != NULL; l = g_list_next(l))
3729 g_list_free(merge_list);
3730 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3734 /* merge by time routine */
3735 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3737 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3740 GList *nearby_tracks;
3742 static guint thr = 1;
3743 guint track_count = 0;
3745 GList *tracks_with_timestamp = NULL;
3746 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3747 if (orig_trk->trackpoints &&
3748 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
3749 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3754 udata.result = &tracks_with_timestamp;
3755 udata.exclude = orig_trk->trackpoints;
3756 udata.with_timestamps = TRUE;
3757 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3758 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3760 if (!tracks_with_timestamp) {
3761 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3764 g_list_free(tracks_with_timestamp);
3766 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3767 _("Merge Threshold..."),
3768 _("Merge when time between tracks less than:"),
3773 /* merge tracks until we can't */
3775 nearby_tracks = NULL;
3779 // Need to refind original track incase we've deleted and recreated it??
3780 //track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3781 trps = orig_trk->trackpoints;
3786 if (nearby_tracks) {
3787 g_list_free(nearby_tracks);
3788 nearby_tracks = NULL;
3791 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3792 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3794 /* g_print("Original track times: %d and %d\n", t1, t2); */
3795 params[0] = &nearby_tracks;
3797 params[2] = GUINT_TO_POINTER (thr);
3799 /* get a list of adjacent-in-time tracks */
3800 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3802 /* add original track */
3803 nearby_tracks = g_list_prepend(nearby_tracks, orig_trk);
3807 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, ((x)->data)))
3808 GList *l = nearby_tracks;
3809 // VikTrack *tr = vik_track_new();
3810 //tr->visible = track->visible;
3814 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3815 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3817 t1 = get_first_trackpoint(l)->timestamp;
3818 t2 = get_last_trackpoint(l)->timestamp;
3819 #undef get_first_trackpoint
3820 #undef get_last_trackpoint
3821 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3825 /* remove trackpoints from merged track, delete track */
3826 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, get_track(l)->trackpoints);
3827 get_track(l)->trackpoints = NULL;
3828 vik_trw_layer_delete_track (vtl, l->data);
3834 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
3835 //vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3838 } while (track_count > 1);
3839 g_list_free(nearby_tracks);
3840 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3843 /* split by time routine */
3844 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3846 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3847 GList *trps = track->trackpoints;
3849 GList *newlists = NULL;
3850 GList *newtps = NULL;
3852 static guint thr = 1;
3859 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3860 _("Split Threshold..."),
3861 _("Split when time between trackpoints exceeds:"),
3866 /* iterate through trackpoints, and copy them into new lists without touching original list */
3867 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3871 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3873 g_print("panic: ts < prev_ts: this should never happen!\n");
3876 if (ts - prev_ts > thr*60) {
3877 /* flush accumulated trackpoints into new list */
3878 newlists = g_list_append(newlists, g_list_reverse(newtps));
3882 /* accumulate trackpoint copies in newtps, in reverse order */
3883 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3885 iter = g_list_next(iter);
3888 newlists = g_list_append(newlists, g_list_reverse(newtps));
3891 /* put lists of trackpoints into tracks */
3894 // Only bother updating if the split results in new tracks
3895 if (g_list_length (newlists) > 1) {
3900 tr = vik_track_new();
3901 tr->visible = track->visible;
3902 tr->trackpoints = (GList *)(iter->data);
3904 new_tr_name = g_strdup_printf ("%s #%d", track->name, i++);
3905 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3906 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3907 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3909 iter = g_list_next(iter);
3911 // Remove original track and then update the display
3912 vik_trw_layer_delete_track (VIK_TRW_LAYER(pass_along[0]), track);
3913 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3915 g_list_free(newlists);
3919 * Split a track by the number of points as specified by the user
3921 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3923 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3924 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3926 // Check valid track
3927 GList *trps = track->trackpoints;
3931 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3932 _("Split Every Nth Point"),
3933 _("Split on every Nth point:"),
3934 250, // Default value as per typical limited track capacity of various GPS devices
3938 // Was a valid number returned?
3944 GList *newlists = NULL;
3945 GList *newtps = NULL;
3950 /* accumulate trackpoint copies in newtps, in reverse order */
3951 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3953 if (count >= points) {
3954 /* flush accumulated trackpoints into new list */
3955 newlists = g_list_append(newlists, g_list_reverse(newtps));
3959 iter = g_list_next(iter);
3962 // If there is a remaining chunk put that into the new split list
3963 // This may well be the whole track if no split points were encountered
3965 newlists = g_list_append(newlists, g_list_reverse(newtps));
3968 /* put lists of trackpoints into tracks */
3971 // Only bother updating if the split results in new tracks
3972 if (g_list_length (newlists) > 1) {
3977 tr = vik_track_new();
3978 tr->visible = track->visible;
3979 tr->trackpoints = (GList *)(iter->data);
3981 new_tr_name = g_strdup_printf ("%s #%d", track->name, i++);
3982 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3984 iter = g_list_next(iter);
3986 // Remove original track and then update the display
3987 vik_trw_layer_delete_track (VIK_TRW_LAYER(pass_along[0]), track);
3988 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3990 g_list_free(newlists);
3993 /* end of split/merge routines */
3998 static void trw_layer_reverse ( gpointer pass_along[6] )
4000 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4001 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4003 // Check valid track
4004 GList *trps = track->trackpoints;
4008 vik_track_reverse ( track );
4010 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4014 * Similar to trw_layer_enum_item, but this uses a sorted method
4017 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4019 GList **list = (GList**)udata;
4020 // *list = g_list_prepend(*all, key); //unsorted method
4021 // Sort named list alphabetically
4022 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4027 * Now Waypoint specific sort
4029 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4031 GList **list = (GList**)udata;
4032 // Sort named list alphabetically
4033 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4037 * Track specific sort
4039 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4041 GList **list = (GList**)udata;
4042 // Sort named list alphabetically
4043 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4048 gboolean has_same_track_name;
4049 const gchar *same_track_name;
4050 } same_track_name_udata;
4052 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4054 const gchar* namea = (const gchar*) aa;
4055 const gchar* nameb = (const gchar*) bb;
4058 gint result = strcmp ( namea, nameb );
4060 if ( result == 0 ) {
4061 // Found two names the same
4062 same_track_name_udata *user_data = udata;
4063 user_data->has_same_track_name = TRUE;
4064 user_data->same_track_name = namea;
4067 // Leave ordering the same
4072 * Find out if any tracks have the same name in this layer
4074 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4076 // Sort items by name, then compare if any next to each other are the same
4078 GList *track_names = NULL;
4079 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4082 if ( ! track_names )
4085 same_track_name_udata udata;
4086 udata.has_same_track_name = FALSE;
4088 // Use sort routine to traverse list comparing items
4089 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4090 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4091 // Still no tracks...
4095 return udata.has_same_track_name;
4099 * Force unqiue track names for this layer
4100 * Note the panel is a required parameter to enable the update of the names displayed
4102 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4104 // . Search list for an instance of repeated name
4105 // . get track of this name
4106 // . create new name
4107 // . rename track & update equiv. treeview iter
4108 // . repeat until all different
4110 same_track_name_udata udata;
4112 GList *track_names = NULL;
4113 udata.has_same_track_name = FALSE;
4114 udata.same_track_name = NULL;
4116 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4119 if ( ! track_names )
4122 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4124 // Still no tracks...
4125 if ( ! dummy_list1 )
4128 while ( udata.has_same_track_name ) {
4130 // Find a track with the same name
4131 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4135 g_critical("Houston, we've had a problem.");
4136 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4137 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4142 gchar *newname = get_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4143 vik_track_set_name ( trk, newname );
4149 // Need want key of it for treeview update
4150 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4152 if ( trkf && udataU.uuid ) {
4154 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4157 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4158 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4159 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4164 // Start trying to find same names again...
4166 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4167 udata.has_same_track_name = FALSE;
4168 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4170 // No tracks any more - give up searching
4171 if ( ! dummy_list2 )
4172 udata.has_same_track_name = FALSE;
4176 vik_layers_panel_emit_update ( vlp );
4182 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4184 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4187 // Ensure list of track names offered is unique
4188 if ( trw_layer_has_same_track_names ( vtl ) ) {
4189 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4190 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4191 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4197 // Sort list alphabetically for better presentation
4198 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4201 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4205 // Get list of items to delete from the user
4206 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4209 _("Delete Selection"),
4210 _("Select tracks to delete"));
4213 // Delete requested tracks
4214 // since specificly requested, IMHO no need for extra confirmation
4215 if ( delete_list ) {
4217 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4218 // This deletes first trk it finds of that name (but uniqueness is enforced above)
4219 trw_layer_delete_track_by_name (vtl, l->data);
4221 g_list_free(delete_list);
4222 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4227 gboolean has_same_waypoint_name;
4228 const gchar *same_waypoint_name;
4229 } same_waypoint_name_udata;
4231 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4233 const gchar* namea = (const gchar*) aa;
4234 const gchar* nameb = (const gchar*) bb;
4237 gint result = strcmp ( namea, nameb );
4239 if ( result == 0 ) {
4240 // Found two names the same
4241 same_waypoint_name_udata *user_data = udata;
4242 user_data->has_same_waypoint_name = TRUE;
4243 user_data->same_waypoint_name = namea;
4246 // Leave ordering the same
4251 * Find out if any waypoints have the same name in this layer
4253 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
4255 // Sort items by name, then compare if any next to each other are the same
4257 GList *waypoint_names = NULL;
4258 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4261 if ( ! waypoint_names )
4264 same_waypoint_name_udata udata;
4265 udata.has_same_waypoint_name = FALSE;
4267 // Use sort routine to traverse list comparing items
4268 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4269 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4270 // Still no waypoints...
4274 return udata.has_same_waypoint_name;
4278 * Force unqiue waypoint names for this layer
4279 * Note the panel is a required parameter to enable the update of the names displayed
4281 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4283 // . Search list for an instance of repeated name
4284 // . get waypoint of this name
4285 // . create new name
4286 // . rename waypoint & update equiv. treeview iter
4287 // . repeat until all different
4289 same_waypoint_name_udata udata;
4291 GList *waypoint_names = NULL;
4292 udata.has_same_waypoint_name = FALSE;
4293 udata.same_waypoint_name = NULL;
4295 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4298 if ( ! waypoint_names )
4301 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4303 // Still no waypoints...
4304 if ( ! dummy_list1 )
4307 while ( udata.has_same_waypoint_name ) {
4309 // Find a waypoint with the same name
4310 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
4314 g_critical("Houston, we've had a problem.");
4315 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4316 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
4321 gchar *newname = get_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
4322 vik_waypoint_set_name ( waypoint, newname );
4325 udataU.wp = waypoint;
4328 // Need want key of it for treeview update
4329 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4331 if ( wpf && udataU.uuid ) {
4333 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4336 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4337 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4338 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4343 // Start trying to find same names again...
4344 waypoint_names = NULL;
4345 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4346 udata.has_same_waypoint_name = FALSE;
4347 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4349 // No waypoints any more - give up searching
4350 if ( ! dummy_list2 )
4351 udata.has_same_waypoint_name = FALSE;
4355 vik_layers_panel_emit_update ( vlp );
4361 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4363 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4366 // Ensure list of waypoint names offered is unique
4367 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
4368 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4369 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4370 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4376 // Sort list alphabetically for better presentation
4377 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4379 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4383 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4385 // Get list of items to delete from the user
4386 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4389 _("Delete Selection"),
4390 _("Select waypoints to delete"));
4393 // Delete requested waypoints
4394 // since specificly requested, IMHO no need for extra confirmation
4395 if ( delete_list ) {
4397 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4398 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
4399 trw_layer_delete_waypoint_by_name (vtl, l->data);
4401 g_list_free(delete_list);
4402 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4407 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4409 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4411 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4414 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4416 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4417 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4421 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4423 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4425 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
4427 // No actual change to the name supplied
4428 if (strcmp(newname, wp->name) == 0 )
4431 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4434 // An existing waypoint has been found with the requested name
4435 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4436 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
4441 // Update WP name and refresh the treeview
4442 vik_waypoint_set_name (wp, newname);
4444 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4445 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4448 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4453 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4455 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
4457 // No actual change to the name supplied
4458 if (strcmp(newname, trk->name) == 0)
4461 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4464 // An existing track has been found with the requested name
4465 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4466 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4470 // Update track name and refresh GUI parts
4471 vik_track_set_name (trk, newname);
4473 // Update any subwindows that could be displaying this track which has changed name
4474 // Only one Track Edit Window
4475 if ( l->current_tp_track == trk && l->tpwin ) {
4476 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
4478 // Property Dialog of the track
4479 vik_trw_layer_propwin_update ( trk );
4481 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4482 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4485 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4492 static gboolean is_valid_geocache_name ( gchar *str )
4494 gint len = strlen ( str );
4495 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]));
4498 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
4500 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4501 a_acquire_set_filter_track ( trk );
4504 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
4506 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
4507 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
4510 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
4512 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4514 gchar *escaped = uri_escape ( tr->comment );
4515 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4516 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4522 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4523 /* viewpoint is now available instead */
4524 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4526 static gpointer pass_along[8];
4528 gboolean rv = FALSE;
4531 pass_along[1] = vlp;
4532 pass_along[2] = GINT_TO_POINTER (subtype);
4533 pass_along[3] = sublayer;
4534 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
4535 pass_along[5] = vvp;
4536 pass_along[6] = iter;
4537 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
4539 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4543 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
4544 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
4545 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4546 gtk_widget_show ( item );
4548 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4549 VikTrwLayer *vtl = l;
4550 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
4551 if (tr && tr->property_dialog)
4552 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
4555 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
4556 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
4557 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4558 gtk_widget_show ( item );
4560 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
4561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
4562 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4563 gtk_widget_show ( item );
4565 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
4566 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
4567 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4568 gtk_widget_show ( item );
4570 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4572 gboolean separator_created = FALSE;
4574 /* could be a right-click using the tool */
4575 if ( vlp != NULL ) {
4576 item = gtk_menu_item_new ();
4577 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4578 gtk_widget_show ( item );
4580 separator_created = TRUE;
4582 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4583 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4584 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4585 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4586 gtk_widget_show ( item );
4589 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4591 if ( wp && wp->name ) {
4592 if ( is_valid_geocache_name ( wp->name ) ) {
4594 if ( !separator_created ) {
4595 item = gtk_menu_item_new ();
4596 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4597 gtk_widget_show ( item );
4598 separator_created = TRUE;
4601 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
4602 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
4603 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4604 gtk_widget_show ( item );
4608 if ( wp && wp->image )
4610 if ( !separator_created ) {
4611 item = gtk_menu_item_new ();
4612 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4613 gtk_widget_show ( item );
4614 separator_created = TRUE;
4617 // Set up image paramater
4618 pass_along[5] = wp->image;
4620 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
4621 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
4622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
4623 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4624 gtk_widget_show ( item );
4626 #ifdef VIK_CONFIG_GEOTAG
4627 GtkWidget *geotag_submenu = gtk_menu_new ();
4628 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
4629 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
4630 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4631 gtk_widget_show ( item );
4632 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
4634 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
4635 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
4636 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4637 gtk_widget_show ( item );
4639 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
4640 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
4641 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4642 gtk_widget_show ( item );
4649 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
4652 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
4653 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4655 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4656 gtk_widget_show ( item );
4659 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
4661 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
4662 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4663 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4664 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4665 gtk_widget_show ( item );
4667 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4668 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4669 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4670 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4671 gtk_widget_show ( item );
4673 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
4674 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4675 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4676 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4677 gtk_widget_show ( item );
4679 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
4680 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4681 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4682 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4683 gtk_widget_show ( item );
4686 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
4690 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
4691 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4693 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4694 gtk_widget_show ( item );
4696 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
4697 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4698 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4699 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4700 gtk_widget_show ( item );
4702 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
4703 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4705 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4706 gtk_widget_show ( item );
4709 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4711 GtkWidget *goto_submenu;
4712 item = gtk_menu_item_new ();
4713 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4714 gtk_widget_show ( item );
4716 goto_submenu = gtk_menu_new ();
4717 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4718 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4719 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4720 gtk_widget_show ( item );
4721 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
4723 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
4724 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
4725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
4726 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4727 gtk_widget_show ( item );
4729 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
4730 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4731 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
4732 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4733 gtk_widget_show ( item );
4735 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
4736 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
4737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
4738 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4739 gtk_widget_show ( item );
4741 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
4742 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
4743 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
4744 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4745 gtk_widget_show ( item );
4747 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4748 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4749 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4750 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4751 gtk_widget_show ( item );
4753 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4754 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4755 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4756 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4757 gtk_widget_show ( item );
4759 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4760 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4761 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4762 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4763 gtk_widget_show ( item );
4765 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4766 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4767 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4768 gtk_widget_show ( item );
4770 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4771 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4772 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4773 gtk_widget_show ( item );
4775 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4776 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4777 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4778 gtk_widget_show ( item );
4780 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4781 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4782 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4783 gtk_widget_show ( item );
4785 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
4786 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
4787 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
4788 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4789 gtk_widget_show ( item );
4791 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4793 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4794 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
4795 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4796 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4797 gtk_widget_show ( item );
4800 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4801 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
4802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4803 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4804 gtk_widget_show ( item );
4806 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4807 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
4809 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4810 gtk_widget_show ( item );
4812 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
4813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4815 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4816 gtk_widget_show ( item );
4818 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4819 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
4820 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4821 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4822 gtk_widget_show ( item );
4824 #ifdef VIK_CONFIG_OPENSTREETMAP
4825 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4826 // Convert internal pointer into actual track for usage outside this file
4827 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
4828 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4829 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4830 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4831 gtk_widget_show ( item );
4834 if ( is_valid_google_route ( l, sublayer ) )
4836 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4837 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4838 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4839 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4840 gtk_widget_show ( item );
4843 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4844 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4846 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4847 gtk_widget_show ( item );
4849 /* ATM This function is only available via the layers panel, due to needing a vlp */
4851 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4852 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4853 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4855 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4856 gtk_widget_show ( item );
4860 #ifdef VIK_CONFIG_GEOTAG
4861 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
4863 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4864 gtk_widget_show ( item );
4867 // Only show on viewport popmenu when a trackpoint is selected
4868 if ( ! vlp && l->current_tpl ) {
4870 item = gtk_menu_item_new ();
4871 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4872 gtk_widget_show ( item );
4874 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4875 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4877 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4878 gtk_widget_show ( item );
4886 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4889 if (!vtl->current_tpl)
4891 if (!vtl->current_tpl->next)
4894 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4895 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4897 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4900 VikTrackpoint *tp_new = vik_trackpoint_new();
4901 struct LatLon ll_current, ll_next;
4902 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4903 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4905 /* main positional interpolation */
4906 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4907 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4909 /* Now other properties that can be interpolated */
4910 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4912 if (tp_current->has_timestamp && tp_next->has_timestamp) {
4913 /* Note here the division is applied to each part, then added
4914 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4915 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4916 tp_new->has_timestamp = TRUE;
4919 if (tp_current->speed != NAN && tp_next->speed != NAN)
4920 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4922 /* TODO - improve interpolation of course, as it may not be correct.
4923 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4924 [similar applies if value is in radians] */
4925 if (tp_current->course != NAN && tp_next->course != NAN)
4926 tp_new->speed = (tp_current->course + tp_next->course)/2;
4928 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4930 /* Insert new point into the trackpoints list after the current TP */
4931 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
4932 gint index = g_list_index ( tr->trackpoints, tp_current );
4934 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4939 /* to be called when last_tpl no longer exists. */
4940 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4942 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4943 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4944 vtl->last_tpl = NULL;
4945 vtl->last_tp_track = NULL;
4948 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4954 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4958 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4960 if ( vtl->current_tpl )
4962 vtl->current_tpl = NULL;
4963 vtl->current_tp_track = NULL;
4964 vtl->current_tp_id = NULL;
4965 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4969 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4971 g_assert ( vtl->tpwin != NULL );
4972 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4973 trw_layer_cancel_current_tp ( vtl, TRUE );
4975 if ( vtl->current_tpl == NULL )
4978 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4980 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4981 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4983 VikTrack *tr = vik_track_new ();
4984 GList *newglist = g_list_alloc ();
4985 newglist->prev = NULL;
4986 newglist->next = vtl->current_tpl->next;
4987 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4988 tr->trackpoints = newglist;
4990 vtl->current_tpl->next->prev = newglist; /* end old track here */
4991 vtl->current_tpl->next = NULL;
4993 vtl->current_tpl = newglist; /* change tp to first of new track. */
4994 vtl->current_tp_track = tr;
4996 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5000 vik_trw_layer_add_track ( vtl, name, tr );
5006 // Also need id of newly created track
5007 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5008 if ( trkf && udata.uuid )
5009 vtl->current_tp_id = udata.uuid;
5011 vtl->current_tp_id = NULL;
5013 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5016 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5018 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5023 /* can't join with a non-existent trackpoint */
5024 vtl->last_tpl = NULL;
5025 vtl->last_tp_track = NULL;
5027 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5029 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5030 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5032 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
5034 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
5035 if ( vtl->current_tp_track )
5036 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
5038 trw_layer_cancel_last_tp ( vtl );
5040 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
5041 g_list_free_1 ( vtl->current_tpl );
5042 vtl->current_tpl = new_tpl;
5043 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5047 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
5048 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
5049 g_list_free_1 ( vtl->current_tpl );
5050 trw_layer_cancel_current_tp ( vtl, FALSE );
5053 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5055 vtl->last_tpl = vtl->current_tpl;
5056 if ( vtl->current_tp_track )
5057 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
5058 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
5060 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5062 vtl->last_tpl = vtl->current_tpl;
5063 if ( vtl->current_tp_track )
5064 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
5065 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5067 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
5069 // Check tracks exist and are different before joining
5070 if ( ! vtl->last_tp_track || ! vtl->current_tp_track || vtl->last_tp_track == vtl->current_tp_track )
5073 VikTrack *tr1 = vtl->last_tp_track;
5074 VikTrack *tr2 = vtl->current_tp_track;
5076 VikTrack *tr_first = tr1, *tr_last = tr2;
5078 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
5079 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
5080 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
5081 vik_track_reverse ( tr1 );
5082 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
5087 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
5089 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. */
5090 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
5091 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
5092 tr2->trackpoints = NULL;
5094 vtl->current_tp_track = vtl->last_tp_track; /* current_tp stays the same (believe it or not!) */
5095 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5097 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
5098 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
5099 vik_trw_layer_delete_track ( vtl, vtl->current_tp_track );
5101 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
5102 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5104 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5106 trw_layer_insert_tp_after_current_tp ( vtl );
5107 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5109 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
5110 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5113 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5117 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5118 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5119 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5120 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5121 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5123 if ( vtl->current_tpl )
5124 if ( vtl->current_tp_track )
5125 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5126 /* set layer name and TP data */
5129 /***************************************************************************
5131 ***************************************************************************/
5133 /*** Utility data structures and functions ****/
5137 gint closest_x, closest_y;
5138 gpointer *closest_wp_id;
5139 VikWaypoint *closest_wp;
5145 gint closest_x, closest_y;
5146 gpointer closest_track_id;
5147 VikTrackpoint *closest_tp;
5152 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5158 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5160 // If waypoint has an image then use the image size to select
5162 gint slackx, slacky;
5163 slackx = wp->image_width / 2;
5164 slacky = wp->image_height / 2;
5166 if ( x <= params->x + slackx && x >= params->x - slackx
5167 && y <= params->y + slacky && y >= params->y - slacky ) {
5168 params->closest_wp_id = id;
5169 params->closest_wp = wp;
5170 params->closest_x = x;
5171 params->closest_y = y;
5174 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5175 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5176 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5178 params->closest_wp_id = id;
5179 params->closest_wp = wp;
5180 params->closest_x = x;
5181 params->closest_y = y;
5185 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5187 GList *tpl = t->trackpoints;
5196 tp = VIK_TRACKPOINT(tpl->data);
5198 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5200 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5201 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5202 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5204 params->closest_track_id = id;
5205 params->closest_tp = tp;
5206 params->closest_tpl = tpl;
5207 params->closest_x = x;
5208 params->closest_y = y;
5214 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5216 TPSearchParams params;
5220 params.closest_track_id = NULL;
5221 params.closest_tp = NULL;
5222 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5223 return params.closest_tp;
5226 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5228 WPSearchParams params;
5232 params.closest_wp = NULL;
5233 params.closest_wp_id = NULL;
5234 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5235 return params.closest_wp;
5239 // Some forward declarations
5240 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5241 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5242 static void marker_end_move ( tool_ed_t *t );
5245 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5249 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5251 // Here always allow snapping back to the original location
5252 // this is useful when one decides not to move the thing afterall
5253 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5256 if ( event->state & GDK_CONTROL_MASK )
5258 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5260 new_coord = tp->coord;
5264 if ( event->state & GDK_SHIFT_MASK )
5266 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5268 new_coord = wp->coord;
5272 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5274 marker_moveto ( t, x, y );
5281 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5283 if ( t->holding && event->button == 1 )
5286 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5289 if ( event->state & GDK_CONTROL_MASK )
5291 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5293 new_coord = tp->coord;
5297 if ( event->state & GDK_SHIFT_MASK )
5299 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5301 new_coord = wp->coord;
5304 marker_end_move ( t );
5306 // Determine if working on a waypoint or a trackpoint
5307 if ( t->is_waypoint )
5308 vtl->current_wp->coord = new_coord;
5310 if ( vtl->current_tpl ) {
5311 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5314 if ( vtl->current_tp_track )
5315 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5317 // Don't really know what this is for but seems like it might be handy...
5318 /* can't join with itself! */
5319 trw_layer_cancel_last_tp ( vtl );
5324 vtl->current_wp = NULL;
5325 vtl->current_wp_id = NULL;
5326 trw_layer_cancel_current_tp ( vtl, FALSE );
5328 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5335 Returns true if a waypoint or track is found near the requested event position for this particular layer
5336 The item found is automatically selected
5337 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5339 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5341 if ( event->button != 1 )
5344 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5347 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5350 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5352 if (vtl->waypoints_visible) {
5353 WPSearchParams wp_params;
5354 wp_params.vvp = vvp;
5355 wp_params.x = event->x;
5356 wp_params.y = event->y;
5357 wp_params.closest_wp_id = NULL;
5358 wp_params.closest_wp = NULL;
5360 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5362 if ( wp_params.closest_wp ) {
5365 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5367 // Too easy to move it so must be holding shift to start immediately moving it
5368 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5369 if ( event->state & GDK_SHIFT_MASK ||
5370 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5371 // Put into 'move buffer'
5372 // NB vvp & vw already set in tet
5373 tet->vtl = (gpointer)vtl;
5374 tet->is_waypoint = TRUE;
5376 marker_begin_move (tet, event->x, event->y);
5379 vtl->current_wp = wp_params.closest_wp;
5380 vtl->current_wp_id = wp_params.closest_wp_id;
5382 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5388 if (vtl->tracks_visible) {
5389 TPSearchParams tp_params;
5390 tp_params.vvp = vvp;
5391 tp_params.x = event->x;
5392 tp_params.y = event->y;
5393 tp_params.closest_track_id = NULL;
5394 tp_params.closest_tp = NULL;
5396 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5398 if ( tp_params.closest_tp ) {
5400 // Always select + highlight the track
5401 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5403 tet->is_waypoint = FALSE;
5405 // Select the Trackpoint
5406 // Can move it immediately when control held or it's the previously selected tp
5407 if ( event->state & GDK_CONTROL_MASK ||
5408 vtl->current_tpl == tp_params.closest_tpl ) {
5409 // Put into 'move buffer'
5410 // NB vvp & vw already set in tet
5411 tet->vtl = (gpointer)vtl;
5412 marker_begin_move (tet, event->x, event->y);
5415 vtl->current_tpl = tp_params.closest_tpl;
5416 vtl->current_tp_id = tp_params.closest_track_id;
5417 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5419 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5422 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5424 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5429 /* these aren't the droids you're looking for */
5430 vtl->current_wp = NULL;
5431 vtl->current_wp_id = NULL;
5432 trw_layer_cancel_current_tp ( vtl, FALSE );
5435 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5440 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5442 if ( event->button != 3 )
5445 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5448 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5451 /* Post menu for the currently selected item */
5453 /* See if a track is selected */
5454 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5455 if ( track && track->visible ) {
5457 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
5459 if ( vtl->track_right_click_menu )
5460 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5462 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5464 trw_layer_sublayer_add_menu_items ( vtl,
5465 vtl->track_right_click_menu,
5467 VIK_TRW_LAYER_SUBLAYER_TRACK,
5468 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
5469 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
5472 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5478 /* See if a waypoint is selected */
5479 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5480 if ( waypoint && waypoint->visible ) {
5481 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
5483 if ( vtl->wp_right_click_menu )
5484 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
5486 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5487 trw_layer_sublayer_add_menu_items ( vtl,
5488 vtl->wp_right_click_menu,
5490 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5491 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
5492 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
5494 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5503 /* background drawing hook, to be passed the viewport */
5504 static gboolean tool_sync_done = TRUE;
5506 static gboolean tool_sync(gpointer data)
5508 VikViewport *vvp = data;
5509 gdk_threads_enter();
5510 vik_viewport_sync(vvp);
5511 tool_sync_done = TRUE;
5512 gdk_threads_leave();
5516 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
5519 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
5520 gdk_gc_set_function ( t->gc, GDK_INVERT );
5521 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5522 vik_viewport_sync(t->vvp);
5527 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
5529 VikViewport *vvp = t->vvp;
5530 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5531 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5535 if (tool_sync_done) {
5536 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
5537 tool_sync_done = FALSE;
5541 static void marker_end_move ( tool_ed_t *t )
5543 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5544 g_object_unref ( t->gc );
5548 /*** Edit waypoint ****/
5550 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5552 tool_ed_t *t = g_new(tool_ed_t, 1);
5558 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5560 WPSearchParams params;
5561 tool_ed_t *t = data;
5562 VikViewport *vvp = t->vvp;
5564 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5571 if ( !vtl->vl.visible || !vtl->waypoints_visible )
5574 if ( vtl->current_wp && vtl->current_wp->visible )
5576 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5578 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5580 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5581 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5583 if ( event->button == 3 )
5584 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5586 marker_begin_move(t, event->x, event->y);
5593 params.x = event->x;
5594 params.y = event->y;
5595 params.closest_wp_id = NULL;
5596 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5597 params.closest_wp = NULL;
5598 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5599 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5601 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5602 marker_begin_move(t, event->x, event->y);
5603 g_critical("shouldn't be here");
5606 else if ( params.closest_wp )
5608 if ( event->button == 3 )
5609 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5611 vtl->waypoint_rightclick = FALSE;
5613 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
5615 vtl->current_wp = params.closest_wp;
5616 vtl->current_wp_id = params.closest_wp_id;
5618 /* could make it so don't update if old WP is off screen and new is null but oh well */
5619 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5623 vtl->current_wp = NULL;
5624 vtl->current_wp_id = NULL;
5625 vtl->waypoint_rightclick = FALSE;
5626 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5630 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5632 tool_ed_t *t = data;
5633 VikViewport *vvp = t->vvp;
5635 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5640 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5643 if ( event->state & GDK_CONTROL_MASK )
5645 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5647 new_coord = tp->coord;
5651 if ( event->state & GDK_SHIFT_MASK )
5653 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5654 if ( wp && wp != vtl->current_wp )
5655 new_coord = wp->coord;
5660 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5662 marker_moveto ( t, x, y );
5669 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5671 tool_ed_t *t = data;
5672 VikViewport *vvp = t->vvp;
5674 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5677 if ( t->holding && event->button == 1 )
5680 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5683 if ( event->state & GDK_CONTROL_MASK )
5685 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5687 new_coord = tp->coord;
5691 if ( event->state & GDK_SHIFT_MASK )
5693 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5694 if ( wp && wp != vtl->current_wp )
5695 new_coord = wp->coord;
5698 marker_end_move ( t );
5700 vtl->current_wp->coord = new_coord;
5701 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5704 /* PUT IN RIGHT PLACE!!! */
5705 if ( event->button == 3 && vtl->waypoint_rightclick )
5707 if ( vtl->wp_right_click_menu )
5708 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5709 if ( vtl->current_wp ) {
5710 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5711 trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_id, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_id ), vvp );
5712 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5714 vtl->waypoint_rightclick = FALSE;
5719 /**** Begin track ***/
5720 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5725 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5727 vtl->current_track = NULL;
5728 return tool_new_track_click ( vtl, event, vvp );
5731 /*** New track ****/
5733 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5741 gint x1,y1,x2,y2,x3,y3;
5743 } new_track_move_passalong_t;
5745 /* sync and undraw, but only when we have time */
5746 static gboolean ct_sync ( gpointer passalong )
5748 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5750 vik_viewport_sync ( p->vvp );
5751 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5752 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5753 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);
5754 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5756 g_free ( (gpointer) p->str ) ;
5757 p->vtl->ct_sync_done = TRUE;
5762 static const gchar* distance_string (gdouble distance)
5766 /* draw label with distance */
5767 vik_units_distance_t dist_units = a_vik_get_units_distance ();
5768 switch (dist_units) {
5769 case VIK_UNITS_DISTANCE_MILES:
5770 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5771 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5772 } else if (distance < 1609.4) {
5773 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5775 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5779 // VIK_UNITS_DISTANCE_KILOMETRES
5780 if (distance >= 1000 && distance < 100000) {
5781 g_sprintf(str, "%3.2f km", distance/1000.0);
5782 } else if (distance < 1000) {
5783 g_sprintf(str, "%d m", (int)distance);
5785 g_sprintf(str, "%d km", (int)distance/1000);
5789 return g_strdup (str);
5793 * Actually set the message in statusbar
5795 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5797 // Only show elevation data when track has some elevation properties
5798 gchar str_gain_loss[64];
5799 str_gain_loss[0] = '\0';
5801 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5802 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5803 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5805 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5808 // Write with full gain/loss information
5809 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5810 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5815 * Figure out what information should be set in the statusbar and then write it
5817 static void update_statusbar ( VikTrwLayer *vtl )
5819 // Get elevation data
5820 gdouble elev_gain, elev_loss;
5821 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5823 /* Find out actual distance of current track */
5824 gdouble distance = vik_track_get_length (vtl->current_track);
5825 const gchar *str = distance_string (distance);
5827 statusbar_write (str, elev_gain, elev_loss, vtl);
5829 g_free ((gpointer)str);
5833 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5835 /* if we haven't sync'ed yet, we don't have time to do more. */
5836 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5837 GList *iter = vtl->current_track->trackpoints;
5838 new_track_move_passalong_t *passalong;
5841 while ( iter->next )
5843 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5844 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5845 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5847 /* Find out actual distance of current track */
5848 gdouble distance = vik_track_get_length (vtl->current_track);
5850 // Now add distance to where the pointer is //
5853 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5854 vik_coord_to_latlon ( &coord, &ll );
5855 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5857 // Get elevation data
5858 gdouble elev_gain, elev_loss;
5859 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5861 // Adjust elevation data (if available) for the current pointer position
5863 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5864 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5865 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5866 // Adjust elevation of last track point
5867 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5869 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5872 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5876 const gchar *str = distance_string (distance);
5878 /* offset from cursor a bit */
5881 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5882 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5884 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5886 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5887 passalong->vtl = vtl;
5888 passalong->vvp = vvp;
5891 passalong->x2 = event->x;
5892 passalong->y2 = event->y;
5895 passalong->str = str;
5897 // Update statusbar with full gain/loss information
5898 statusbar_write (str, elev_gain, elev_loss, vtl);
5900 /* this will sync and undraw when we have time to */
5901 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5902 vtl->ct_sync_done = FALSE;
5903 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5905 return VIK_LAYER_TOOL_ACK;
5908 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5910 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5911 vtl->current_track = NULL;
5912 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5914 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5916 if ( vtl->current_track->trackpoints )
5918 GList *last = g_list_last(vtl->current_track->trackpoints);
5919 g_free ( last->data );
5920 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5923 update_statusbar ( vtl );
5925 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5931 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5935 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5938 if ( event->button == 3 && vtl->current_track )
5941 if ( vtl->current_track->trackpoints )
5943 GList *last = g_list_last(vtl->current_track->trackpoints);
5944 g_free ( last->data );
5945 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5947 update_statusbar ( vtl );
5949 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5953 if ( event->type == GDK_2BUTTON_PRESS )
5955 /* subtract last (duplicate from double click) tp then end */
5956 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5958 GList *last = g_list_last(vtl->current_track->trackpoints);
5959 g_free ( last->data );
5960 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5961 /* undo last, then end */
5962 vtl->current_track = NULL;
5964 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5968 if ( ! vtl->current_track )
5970 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5971 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5973 vtl->current_track = vik_track_new();
5974 vtl->current_track->visible = TRUE;
5975 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5977 /* incase it was created by begin track */
5978 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5983 tp = vik_trackpoint_new();
5984 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5986 /* snap to other TP */
5987 if ( event->state & GDK_CONTROL_MASK )
5989 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5991 tp->coord = other_tp->coord;
5994 tp->newsegment = FALSE;
5995 tp->has_timestamp = FALSE;
5997 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5998 /* Auto attempt to get elevation from DEM data (if it's available) */
5999 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
6001 vtl->ct_x1 = vtl->ct_x2;
6002 vtl->ct_y1 = vtl->ct_y2;
6003 vtl->ct_x2 = event->x;
6004 vtl->ct_y2 = event->y;
6006 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6010 /*** New waypoint ****/
6012 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6017 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6020 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6022 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6023 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
6024 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6029 /*** Edit trackpoint ****/
6031 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6033 tool_ed_t *t = g_new(tool_ed_t, 1);
6039 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6041 tool_ed_t *t = data;
6042 VikViewport *vvp = t->vvp;
6043 TPSearchParams params;
6044 /* OUTDATED DOCUMENTATION:
6045 find 5 pixel range on each side. then put these UTM, and a pointer
6046 to the winning track name (and maybe the winning track itself), and a
6047 pointer to the winning trackpoint, inside an array or struct. pass
6048 this along, do a foreach on the tracks which will do a foreach on the
6051 params.x = event->x;
6052 params.y = event->y;
6053 params.closest_track_id = NULL;
6054 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6055 params.closest_tp = NULL;
6057 if ( event->button != 1 )
6060 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6063 if ( !vtl->vl.visible || !vtl->tracks_visible )
6066 if ( vtl->current_tpl )
6068 /* 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.) */
6069 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6070 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6075 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6077 if ( current_tr->visible &&
6078 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
6079 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6080 marker_begin_move ( t, event->x, event->y );
6084 vtl->last_tpl = vtl->current_tpl;
6085 vtl->last_tp_track = vtl->current_tp_track;
6088 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6090 if ( params.closest_tp )
6092 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
6093 vtl->current_tpl = params.closest_tpl;
6094 vtl->current_tp_id = params.closest_track_id;
6095 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
6096 trw_layer_tpwin_init ( vtl );
6097 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
6098 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6102 /* these aren't the droids you're looking for */
6106 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6108 tool_ed_t *t = data;
6109 VikViewport *vvp = t->vvp;
6111 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6117 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6120 if ( event->state & GDK_CONTROL_MASK )
6122 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6123 if ( tp && tp != vtl->current_tpl->data )
6124 new_coord = tp->coord;
6126 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6129 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6130 marker_moveto ( t, x, y );
6138 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6140 tool_ed_t *t = data;
6141 VikViewport *vvp = t->vvp;
6143 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6145 if ( event->button != 1)
6150 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6153 if ( event->state & GDK_CONTROL_MASK )
6155 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6156 if ( tp && tp != vtl->current_tpl->data )
6157 new_coord = tp->coord;
6160 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6162 marker_end_move ( t );
6164 /* diff dist is diff from orig */
6166 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6167 /* can't join with itself! */
6168 trw_layer_cancel_last_tp ( vtl );
6170 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6177 /*** Route Finder ***/
6178 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6183 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6186 if ( !vtl ) return FALSE;
6187 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6188 if ( event->button == 3 && vtl->route_finder_current_track ) {
6190 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6192 vtl->route_finder_coord = *new_end;
6194 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6195 /* remove last ' to:...' */
6196 if ( vtl->route_finder_current_track->comment ) {
6197 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6198 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6199 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6200 last_to - vtl->route_finder_current_track->comment - 1);
6201 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6206 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6207 struct LatLon start, end;
6208 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6209 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6212 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6213 vik_coord_to_latlon ( &(tmp), &end );
6214 vtl->route_finder_coord = tmp; /* for continuations */
6216 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6217 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6218 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6220 vtl->route_finder_check_added_track = TRUE;
6221 vtl->route_finder_started = FALSE;
6224 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6225 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6226 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6227 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6228 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6229 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
6232 /* see if anything was done -- a track was added or appended to */
6233 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6234 vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
6235 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6236 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6237 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6238 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6240 vtl->route_finder_added_track = NULL;
6241 vtl->route_finder_check_added_track = FALSE;
6242 vtl->route_finder_append = FALSE;
6244 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6246 vtl->route_finder_started = TRUE;
6247 vtl->route_finder_coord = tmp;
6248 vtl->route_finder_current_track = NULL;
6253 /*** Show picture ****/
6255 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6260 /* Params are: vvp, event, last match found or NULL */
6261 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6263 if ( wp->image && wp->visible )
6265 gint x, y, slackx, slacky;
6266 GdkEventButton *event = (GdkEventButton *) params[1];
6268 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6269 slackx = wp->image_width / 2;
6270 slacky = wp->image_height / 2;
6271 if ( x <= event->x + slackx && x >= event->x - slackx
6272 && y <= event->y + slacky && y >= event->y - slacky )
6274 params[2] = wp->image; /* we've found a match. however continue searching
6275 * since we want to find the last match -- that
6276 * is, the match that was drawn last. */
6281 static void trw_layer_show_picture ( gpointer pass_along[6] )
6283 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6285 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
6288 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6289 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6290 g_free ( quoted_file );
6291 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6293 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() );
6294 g_error_free ( err );
6297 #endif /* WINDOWS */
6300 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6302 gpointer params[3] = { vvp, event, NULL };
6303 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6305 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6308 static gpointer pass_along[6];
6309 pass_along[0] = vtl;
6310 pass_along[5] = params[2];
6311 trw_layer_show_picture ( pass_along );
6312 return TRUE; /* found a match */
6315 return FALSE; /* go through other layers, searching for a match */
6318 /***************************************************************************
6320 ***************************************************************************/
6326 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6328 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6329 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6332 /* Structure for thumbnail creating data used in the background thread */
6334 VikTrwLayer *vtl; // Layer needed for redrawing
6335 GSList *pics; // Image list
6336 } thumbnail_create_thread_data;
6338 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6340 guint total = g_slist_length(tctd->pics), done = 0;
6341 while ( tctd->pics )
6343 a_thumbnails_create ( (gchar *) tctd->pics->data );
6344 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6346 return -1; /* Abort thread */
6348 tctd->pics = tctd->pics->next;
6351 // Redraw to show the thumbnails as they are now created
6352 if ( IS_VIK_LAYER(tctd->vtl) )
6353 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6358 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6360 while ( tctd->pics )
6362 g_free ( tctd->pics->data );
6363 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6368 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6370 if ( ! vtl->has_verified_thumbnails )
6372 GSList *pics = NULL;
6373 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6376 gint len = g_slist_length ( pics );
6377 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6378 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6381 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6383 (vik_thr_func) create_thumbnails_thread,
6385 (vik_thr_free_func) thumbnail_create_thread_free,
6393 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6395 return vtl->coord_mode;
6399 * Uniquify the whole layer
6400 * Also requires the layers panel as the names shown there need updating too
6401 * Returns whether the operation was successful or not
6403 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6406 vik_trw_layer_uniquify_tracks ( vtl, vlp );
6407 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
6413 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
6415 vik_coord_convert ( &(wp->coord), *dest_mode );
6418 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6420 vik_track_convert ( tr, *dest_mode );
6423 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6425 if ( vtl->coord_mode != dest_mode )
6427 vtl->coord_mode = dest_mode;
6428 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6429 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6433 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
6435 vtl->menu_selection = selection;
6438 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
6440 return (vtl->menu_selection);
6443 /* ----------- Downloading maps along tracks --------------- */
6445 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6447 /* TODO: calculating based on current size of viewport */
6448 const gdouble w_at_zoom_0_125 = 0.0013;
6449 const gdouble h_at_zoom_0_125 = 0.0011;
6450 gdouble zoom_factor = zoom_level/0.125;
6452 wh->lat = h_at_zoom_0_125 * zoom_factor;
6453 wh->lon = w_at_zoom_0_125 * zoom_factor;
6455 return 0; /* all OK */
6458 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6460 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6461 (dist->lat >= ABS(to->north_south - from->north_south)))
6464 VikCoord *coord = g_malloc(sizeof(VikCoord));
6465 coord->mode = VIK_COORD_LATLON;
6467 if (ABS(gradient) < 1) {
6468 if (from->east_west > to->east_west)
6469 coord->east_west = from->east_west - dist->lon;
6471 coord->east_west = from->east_west + dist->lon;
6472 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
6474 if (from->north_south > to->north_south)
6475 coord->north_south = from->north_south - dist->lat;
6477 coord->north_south = from->north_south + dist->lat;
6478 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
6484 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
6486 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
6487 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
6489 VikCoord *next = from;
6491 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
6493 list = g_list_prepend(list, next);
6499 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
6501 typedef struct _Rect {
6506 #define GLRECT(iter) ((Rect *)((iter)->data))
6509 GList *rects_to_download = NULL;
6512 if (get_download_area_width(vvp, zoom_level, &wh))
6515 GList *iter = tr->trackpoints;
6519 gboolean new_map = TRUE;
6520 VikCoord *cur_coord, tl, br;
6523 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
6525 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6526 rect = g_malloc(sizeof(Rect));
6529 rect->center = *cur_coord;
6530 rects_to_download = g_list_prepend(rects_to_download, rect);
6535 gboolean found = FALSE;
6536 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6537 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
6548 GList *fillins = NULL;
6549 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
6550 /* seems that ATM the function get_next_coord works only for LATLON */
6551 if ( cur_coord->mode == VIK_COORD_LATLON ) {
6552 /* fill-ins for far apart points */
6553 GList *cur_rect, *next_rect;
6554 for (cur_rect = rects_to_download;
6555 (next_rect = cur_rect->next) != NULL;
6556 cur_rect = cur_rect->next) {
6557 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
6558 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
6559 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
6563 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
6566 GList *iter = fillins;
6568 cur_coord = (VikCoord *)(iter->data);
6569 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6570 rect = g_malloc(sizeof(Rect));
6573 rect->center = *cur_coord;
6574 rects_to_download = g_list_prepend(rects_to_download, rect);
6579 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6580 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
6584 for (iter = fillins; iter; iter = iter->next)
6586 g_list_free(fillins);
6588 if (rects_to_download) {
6589 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
6590 g_free(rect_iter->data);
6591 g_list_free(rects_to_download);
6595 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6598 gint selected_map, default_map;
6599 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6600 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6601 gint selected_zoom, default_zoom;
6605 VikTrwLayer *vtl = pass_along[0];
6606 VikLayersPanel *vlp = pass_along[1];
6607 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6608 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6610 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6611 int num_maps = g_list_length(vmls);
6614 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6618 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6619 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6621 gchar **np = map_names;
6622 VikMapsLayer **lp = map_layers;
6623 for (i = 0; i < num_maps; i++) {
6624 gboolean dup = FALSE;
6625 vml = (VikMapsLayer *)(vmls->data);
6626 for (j = 0; j < i; j++) { /* no duplicate allowed */
6627 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6634 *np++ = vik_maps_layer_get_map_label(vml);
6640 num_maps = lp - map_layers;
6642 for (default_map = 0; default_map < num_maps; default_map++) {
6643 /* TODO: check for parent layer's visibility */
6644 if (VIK_LAYER(map_layers[default_map])->visible)
6647 default_map = (default_map == num_maps) ? 0 : default_map;
6649 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6650 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6651 if (cur_zoom == zoom_vals[default_zoom])
6654 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6656 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6659 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6662 for (i = 0; i < num_maps; i++)
6663 g_free(map_names[i]);
6671 /**** lowest waypoint number calculation ***/
6672 static gint highest_wp_number_name_to_number(const gchar *name) {
6673 if ( strlen(name) == 3 ) {
6675 if ( n < 100 && name[0] != '0' )
6677 if ( n < 10 && name[0] != '0' )
6685 static void highest_wp_number_reset(VikTrwLayer *vtl)
6687 vtl->highest_wp_number = -1;
6690 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6692 /* if is bigger that top, add it */
6693 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6694 if ( new_wp_num > vtl->highest_wp_number )
6695 vtl->highest_wp_number = new_wp_num;
6698 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6700 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6701 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6702 if ( vtl->highest_wp_number == old_wp_num ) {
6704 vtl->highest_wp_number--;
6706 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6707 /* search down until we find something that *does* exist */
6709 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
6710 vtl->highest_wp_number--;
6711 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6716 /* get lowest unused number */
6717 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6720 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6722 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6723 return g_strdup(buf);