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->name = name;
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 );
5001 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5004 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5006 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5011 /* can't join with a non-existent trackpoint */
5012 vtl->last_tpl = NULL;
5013 vtl->last_tp_track = NULL;
5015 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5017 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5018 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5020 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
5022 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
5023 if ( vtl->current_tp_track )
5024 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
5026 trw_layer_cancel_last_tp ( vtl );
5028 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
5029 g_list_free_1 ( vtl->current_tpl );
5030 vtl->current_tpl = new_tpl;
5031 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5035 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
5036 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
5037 g_list_free_1 ( vtl->current_tpl );
5038 trw_layer_cancel_current_tp ( vtl, FALSE );
5041 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5043 vtl->last_tpl = vtl->current_tpl;
5044 if ( vtl->current_tp_track )
5045 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
5046 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
5048 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5050 vtl->last_tpl = vtl->current_tpl;
5051 if ( vtl->current_tp_track )
5052 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
5053 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5055 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
5057 // Check tracks exist and are different before joining
5058 if ( ! vtl->last_tp_track || ! vtl->current_tp_track || vtl->last_tp_track == vtl->current_tp_track )
5061 VikTrack *tr1 = vtl->last_tp_track;
5062 VikTrack *tr2 = vtl->current_tp_track;
5064 VikTrack *tr_first = tr1, *tr_last = tr2;
5066 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
5067 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
5068 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
5069 vik_track_reverse ( tr1 );
5070 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
5075 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
5077 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. */
5078 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
5079 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
5080 tr2->trackpoints = NULL;
5082 vtl->current_tp_track = vtl->last_tp_track; /* current_tp stays the same (believe it or not!) */
5083 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5085 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
5086 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
5087 vik_trw_layer_delete_track ( vtl, vtl->current_tp_track );
5089 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
5090 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5092 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5094 trw_layer_insert_tp_after_current_tp ( vtl );
5095 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5097 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
5098 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5101 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5105 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5106 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5107 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5108 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5109 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5111 if ( vtl->current_tpl )
5112 if ( vtl->current_tp_track )
5113 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5114 /* set layer name and TP data */
5117 /***************************************************************************
5119 ***************************************************************************/
5121 /*** Utility data structures and functions ****/
5125 gint closest_x, closest_y;
5126 gpointer *closest_wp_id;
5127 VikWaypoint *closest_wp;
5133 gint closest_x, closest_y;
5134 gpointer closest_track_id;
5135 VikTrackpoint *closest_tp;
5140 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5146 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5148 // If waypoint has an image then use the image size to select
5150 gint slackx, slacky;
5151 slackx = wp->image_width / 2;
5152 slacky = wp->image_height / 2;
5154 if ( x <= params->x + slackx && x >= params->x - slackx
5155 && y <= params->y + slacky && y >= params->y - slacky ) {
5156 params->closest_wp_id = id;
5157 params->closest_wp = wp;
5158 params->closest_x = x;
5159 params->closest_y = y;
5162 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5163 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5164 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5166 params->closest_wp_id = id;
5167 params->closest_wp = wp;
5168 params->closest_x = x;
5169 params->closest_y = y;
5173 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5175 GList *tpl = t->trackpoints;
5184 tp = VIK_TRACKPOINT(tpl->data);
5186 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5188 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5189 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5190 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5192 params->closest_track_id = id;
5193 params->closest_tp = tp;
5194 params->closest_tpl = tpl;
5195 params->closest_x = x;
5196 params->closest_y = y;
5202 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5204 TPSearchParams params;
5208 params.closest_track_id = NULL;
5209 params.closest_tp = NULL;
5210 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5211 return params.closest_tp;
5214 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5216 WPSearchParams params;
5220 params.closest_wp = NULL;
5221 params.closest_wp_id = NULL;
5222 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5223 return params.closest_wp;
5227 // Some forward declarations
5228 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5229 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5230 static void marker_end_move ( tool_ed_t *t );
5233 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5237 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5239 // Here always allow snapping back to the original location
5240 // this is useful when one decides not to move the thing afterall
5241 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5244 if ( event->state & GDK_CONTROL_MASK )
5246 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5248 new_coord = tp->coord;
5252 if ( event->state & GDK_SHIFT_MASK )
5254 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5256 new_coord = wp->coord;
5260 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5262 marker_moveto ( t, x, y );
5269 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5271 if ( t->holding && event->button == 1 )
5274 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5277 if ( event->state & GDK_CONTROL_MASK )
5279 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5281 new_coord = tp->coord;
5285 if ( event->state & GDK_SHIFT_MASK )
5287 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5289 new_coord = wp->coord;
5292 marker_end_move ( t );
5294 // Determine if working on a waypoint or a trackpoint
5295 if ( t->is_waypoint )
5296 vtl->current_wp->coord = new_coord;
5298 if ( vtl->current_tpl ) {
5299 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5302 if ( vtl->current_tp_track )
5303 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5305 // Don't really know what this is for but seems like it might be handy...
5306 /* can't join with itself! */
5307 trw_layer_cancel_last_tp ( vtl );
5312 vtl->current_wp = NULL;
5313 vtl->current_wp_id = NULL;
5314 trw_layer_cancel_current_tp ( vtl, FALSE );
5316 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5323 Returns true if a waypoint or track is found near the requested event position for this particular layer
5324 The item found is automatically selected
5325 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5327 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5329 if ( event->button != 1 )
5332 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5335 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5338 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5340 if (vtl->waypoints_visible) {
5341 WPSearchParams wp_params;
5342 wp_params.vvp = vvp;
5343 wp_params.x = event->x;
5344 wp_params.y = event->y;
5345 wp_params.closest_wp_id = NULL;
5346 wp_params.closest_wp = NULL;
5348 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5350 if ( wp_params.closest_wp ) {
5353 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5355 // Too easy to move it so must be holding shift to start immediately moving it
5356 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5357 if ( event->state & GDK_SHIFT_MASK ||
5358 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5359 // Put into 'move buffer'
5360 // NB vvp & vw already set in tet
5361 tet->vtl = (gpointer)vtl;
5362 tet->is_waypoint = TRUE;
5364 marker_begin_move (tet, event->x, event->y);
5367 vtl->current_wp = wp_params.closest_wp;
5368 vtl->current_wp_id = wp_params.closest_wp_id;
5370 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5376 if (vtl->tracks_visible) {
5377 TPSearchParams tp_params;
5378 tp_params.vvp = vvp;
5379 tp_params.x = event->x;
5380 tp_params.y = event->y;
5381 tp_params.closest_track_id = NULL;
5382 tp_params.closest_tp = NULL;
5384 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5386 if ( tp_params.closest_tp ) {
5388 // Always select + highlight the track
5389 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5391 tet->is_waypoint = FALSE;
5393 // Select the Trackpoint
5394 // Can move it immediately when control held or it's the previously selected tp
5395 if ( event->state & GDK_CONTROL_MASK ||
5396 vtl->current_tpl == tp_params.closest_tpl ) {
5397 // Put into 'move buffer'
5398 // NB vvp & vw already set in tet
5399 tet->vtl = (gpointer)vtl;
5400 marker_begin_move (tet, event->x, event->y);
5403 vtl->current_tpl = tp_params.closest_tpl;
5404 vtl->current_tp_id = tp_params.closest_track_id;
5405 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5407 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5410 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5412 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5417 /* these aren't the droids you're looking for */
5418 vtl->current_wp = NULL;
5419 vtl->current_wp_id = NULL;
5420 trw_layer_cancel_current_tp ( vtl, FALSE );
5423 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5428 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5430 if ( event->button != 3 )
5433 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5436 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5439 /* Post menu for the currently selected item */
5441 /* See if a track is selected */
5442 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5443 if ( track && track->visible ) {
5445 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
5447 if ( vtl->track_right_click_menu )
5448 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5450 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5452 trw_layer_sublayer_add_menu_items ( vtl,
5453 vtl->track_right_click_menu,
5455 VIK_TRW_LAYER_SUBLAYER_TRACK,
5456 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
5457 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
5460 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5466 /* See if a waypoint is selected */
5467 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5468 if ( waypoint && waypoint->visible ) {
5469 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
5471 if ( vtl->wp_right_click_menu )
5472 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
5474 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5475 trw_layer_sublayer_add_menu_items ( vtl,
5476 vtl->wp_right_click_menu,
5478 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5479 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
5480 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
5482 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5491 /* background drawing hook, to be passed the viewport */
5492 static gboolean tool_sync_done = TRUE;
5494 static gboolean tool_sync(gpointer data)
5496 VikViewport *vvp = data;
5497 gdk_threads_enter();
5498 vik_viewport_sync(vvp);
5499 tool_sync_done = TRUE;
5500 gdk_threads_leave();
5504 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
5507 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
5508 gdk_gc_set_function ( t->gc, GDK_INVERT );
5509 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5510 vik_viewport_sync(t->vvp);
5515 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
5517 VikViewport *vvp = t->vvp;
5518 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5519 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5523 if (tool_sync_done) {
5524 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
5525 tool_sync_done = FALSE;
5529 static void marker_end_move ( tool_ed_t *t )
5531 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5532 g_object_unref ( t->gc );
5536 /*** Edit waypoint ****/
5538 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5540 tool_ed_t *t = g_new(tool_ed_t, 1);
5546 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5548 WPSearchParams params;
5549 tool_ed_t *t = data;
5550 VikViewport *vvp = t->vvp;
5552 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5559 if ( !vtl->vl.visible || !vtl->waypoints_visible )
5562 if ( vtl->current_wp && vtl->current_wp->visible )
5564 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5566 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5568 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5569 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5571 if ( event->button == 3 )
5572 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5574 marker_begin_move(t, event->x, event->y);
5581 params.x = event->x;
5582 params.y = event->y;
5583 params.closest_wp_id = NULL;
5584 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5585 params.closest_wp = NULL;
5586 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5587 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5589 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5590 marker_begin_move(t, event->x, event->y);
5591 g_critical("shouldn't be here");
5594 else if ( params.closest_wp )
5596 if ( event->button == 3 )
5597 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5599 vtl->waypoint_rightclick = FALSE;
5601 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
5603 vtl->current_wp = params.closest_wp;
5604 vtl->current_wp_id = params.closest_wp_id;
5606 /* could make it so don't update if old WP is off screen and new is null but oh well */
5607 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5611 vtl->current_wp = NULL;
5612 vtl->current_wp_id = NULL;
5613 vtl->waypoint_rightclick = FALSE;
5614 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5618 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5620 tool_ed_t *t = data;
5621 VikViewport *vvp = t->vvp;
5623 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5628 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5631 if ( event->state & GDK_CONTROL_MASK )
5633 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5635 new_coord = tp->coord;
5639 if ( event->state & GDK_SHIFT_MASK )
5641 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5642 if ( wp && wp != vtl->current_wp )
5643 new_coord = wp->coord;
5648 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5650 marker_moveto ( t, x, y );
5657 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5659 tool_ed_t *t = data;
5660 VikViewport *vvp = t->vvp;
5662 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5665 if ( t->holding && event->button == 1 )
5668 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5671 if ( event->state & GDK_CONTROL_MASK )
5673 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5675 new_coord = tp->coord;
5679 if ( event->state & GDK_SHIFT_MASK )
5681 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5682 if ( wp && wp != vtl->current_wp )
5683 new_coord = wp->coord;
5686 marker_end_move ( t );
5688 vtl->current_wp->coord = new_coord;
5689 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5692 /* PUT IN RIGHT PLACE!!! */
5693 if ( event->button == 3 && vtl->waypoint_rightclick )
5695 if ( vtl->wp_right_click_menu )
5696 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5697 if ( vtl->current_wp ) {
5698 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5699 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 );
5700 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5702 vtl->waypoint_rightclick = FALSE;
5707 /**** Begin track ***/
5708 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5713 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5715 vtl->current_track = NULL;
5716 return tool_new_track_click ( vtl, event, vvp );
5719 /*** New track ****/
5721 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5729 gint x1,y1,x2,y2,x3,y3;
5731 } new_track_move_passalong_t;
5733 /* sync and undraw, but only when we have time */
5734 static gboolean ct_sync ( gpointer passalong )
5736 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5738 vik_viewport_sync ( p->vvp );
5739 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5740 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5741 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);
5742 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5744 g_free ( (gpointer) p->str ) ;
5745 p->vtl->ct_sync_done = TRUE;
5750 static const gchar* distance_string (gdouble distance)
5754 /* draw label with distance */
5755 vik_units_distance_t dist_units = a_vik_get_units_distance ();
5756 switch (dist_units) {
5757 case VIK_UNITS_DISTANCE_MILES:
5758 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5759 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5760 } else if (distance < 1609.4) {
5761 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5763 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5767 // VIK_UNITS_DISTANCE_KILOMETRES
5768 if (distance >= 1000 && distance < 100000) {
5769 g_sprintf(str, "%3.2f km", distance/1000.0);
5770 } else if (distance < 1000) {
5771 g_sprintf(str, "%d m", (int)distance);
5773 g_sprintf(str, "%d km", (int)distance/1000);
5777 return g_strdup (str);
5781 * Actually set the message in statusbar
5783 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5785 // Only show elevation data when track has some elevation properties
5786 gchar str_gain_loss[64];
5787 str_gain_loss[0] = '\0';
5789 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5790 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5791 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5793 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5796 // Write with full gain/loss information
5797 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5798 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5803 * Figure out what information should be set in the statusbar and then write it
5805 static void update_statusbar ( VikTrwLayer *vtl )
5807 // Get elevation data
5808 gdouble elev_gain, elev_loss;
5809 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5811 /* Find out actual distance of current track */
5812 gdouble distance = vik_track_get_length (vtl->current_track);
5813 const gchar *str = distance_string (distance);
5815 statusbar_write (str, elev_gain, elev_loss, vtl);
5817 g_free ((gpointer)str);
5821 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5823 /* if we haven't sync'ed yet, we don't have time to do more. */
5824 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5825 GList *iter = vtl->current_track->trackpoints;
5826 new_track_move_passalong_t *passalong;
5829 while ( iter->next )
5831 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5832 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5833 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5835 /* Find out actual distance of current track */
5836 gdouble distance = vik_track_get_length (vtl->current_track);
5838 // Now add distance to where the pointer is //
5841 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5842 vik_coord_to_latlon ( &coord, &ll );
5843 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5845 // Get elevation data
5846 gdouble elev_gain, elev_loss;
5847 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5849 // Adjust elevation data (if available) for the current pointer position
5851 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5852 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5853 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5854 // Adjust elevation of last track point
5855 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5857 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5860 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5864 const gchar *str = distance_string (distance);
5866 /* offset from cursor a bit */
5869 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5870 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5872 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5874 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5875 passalong->vtl = vtl;
5876 passalong->vvp = vvp;
5879 passalong->x2 = event->x;
5880 passalong->y2 = event->y;
5883 passalong->str = str;
5885 // Update statusbar with full gain/loss information
5886 statusbar_write (str, elev_gain, elev_loss, vtl);
5888 /* this will sync and undraw when we have time to */
5889 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5890 vtl->ct_sync_done = FALSE;
5891 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5893 return VIK_LAYER_TOOL_ACK;
5896 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5898 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5899 vtl->current_track = NULL;
5900 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5902 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5904 if ( vtl->current_track->trackpoints )
5906 GList *last = g_list_last(vtl->current_track->trackpoints);
5907 g_free ( last->data );
5908 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5911 update_statusbar ( vtl );
5913 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5919 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5923 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5926 if ( event->button == 3 && vtl->current_track )
5929 if ( vtl->current_track->trackpoints )
5931 GList *last = g_list_last(vtl->current_track->trackpoints);
5932 g_free ( last->data );
5933 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5935 update_statusbar ( vtl );
5937 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5941 if ( event->type == GDK_2BUTTON_PRESS )
5943 /* subtract last (duplicate from double click) tp then end */
5944 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5946 GList *last = g_list_last(vtl->current_track->trackpoints);
5947 g_free ( last->data );
5948 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5949 /* undo last, then end */
5950 vtl->current_track = NULL;
5952 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5956 if ( ! vtl->current_track )
5958 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5959 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5961 vtl->current_track = vik_track_new();
5962 vtl->current_track->visible = TRUE;
5963 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5965 /* incase it was created by begin track */
5966 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5971 tp = vik_trackpoint_new();
5972 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5974 /* snap to other TP */
5975 if ( event->state & GDK_CONTROL_MASK )
5977 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5979 tp->coord = other_tp->coord;
5982 tp->newsegment = FALSE;
5983 tp->has_timestamp = FALSE;
5985 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5986 /* Auto attempt to get elevation from DEM data (if it's available) */
5987 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5989 vtl->ct_x1 = vtl->ct_x2;
5990 vtl->ct_y1 = vtl->ct_y2;
5991 vtl->ct_x2 = event->x;
5992 vtl->ct_y2 = event->y;
5994 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5998 /*** New waypoint ****/
6000 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6005 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6008 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6010 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6011 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
6012 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6017 /*** Edit trackpoint ****/
6019 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6021 tool_ed_t *t = g_new(tool_ed_t, 1);
6027 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6029 tool_ed_t *t = data;
6030 VikViewport *vvp = t->vvp;
6031 TPSearchParams params;
6032 /* OUTDATED DOCUMENTATION:
6033 find 5 pixel range on each side. then put these UTM, and a pointer
6034 to the winning track name (and maybe the winning track itself), and a
6035 pointer to the winning trackpoint, inside an array or struct. pass
6036 this along, do a foreach on the tracks which will do a foreach on the
6039 params.x = event->x;
6040 params.y = event->y;
6041 params.closest_track_id = NULL;
6042 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6043 params.closest_tp = NULL;
6045 if ( event->button != 1 )
6048 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6051 if ( !vtl->vl.visible || !vtl->tracks_visible )
6054 if ( vtl->current_tpl )
6056 /* 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.) */
6057 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6058 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6063 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6065 if ( current_tr->visible &&
6066 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
6067 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6068 marker_begin_move ( t, event->x, event->y );
6072 vtl->last_tpl = vtl->current_tpl;
6073 vtl->last_tp_track = vtl->current_tp_track;
6076 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6078 if ( params.closest_tp )
6080 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
6081 vtl->current_tpl = params.closest_tpl;
6082 vtl->current_tp_id = params.closest_track_id;
6083 trw_layer_tpwin_init ( vtl );
6084 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
6085 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6089 /* these aren't the droids you're looking for */
6093 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
6095 tool_ed_t *t = data;
6096 VikViewport *vvp = t->vvp;
6098 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6104 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6107 if ( event->state & GDK_CONTROL_MASK )
6109 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6110 if ( tp && tp != vtl->current_tpl->data )
6111 new_coord = tp->coord;
6113 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6116 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6117 marker_moveto ( t, x, y );
6125 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6127 tool_ed_t *t = data;
6128 VikViewport *vvp = t->vvp;
6130 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6132 if ( event->button != 1)
6137 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6140 if ( event->state & GDK_CONTROL_MASK )
6142 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6143 if ( tp && tp != vtl->current_tpl->data )
6144 new_coord = tp->coord;
6147 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6149 marker_end_move ( t );
6151 /* diff dist is diff from orig */
6153 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6154 /* can't join with itself! */
6155 trw_layer_cancel_last_tp ( vtl );
6157 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6164 /*** Route Finder ***/
6165 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6170 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6173 if ( !vtl ) return FALSE;
6174 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6175 if ( event->button == 3 && vtl->route_finder_current_track ) {
6177 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6179 vtl->route_finder_coord = *new_end;
6181 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6182 /* remove last ' to:...' */
6183 if ( vtl->route_finder_current_track->comment ) {
6184 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6185 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6186 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6187 last_to - vtl->route_finder_current_track->comment - 1);
6188 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6193 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6194 struct LatLon start, end;
6195 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6196 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6199 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6200 vik_coord_to_latlon ( &(tmp), &end );
6201 vtl->route_finder_coord = tmp; /* for continuations */
6203 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6204 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6205 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6207 vtl->route_finder_check_added_track = TRUE;
6208 vtl->route_finder_started = FALSE;
6211 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6212 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6213 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6214 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6215 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6216 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
6219 /* see if anything was done -- a track was added or appended to */
6220 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6221 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 ) );
6222 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6223 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6224 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6225 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6227 vtl->route_finder_added_track = NULL;
6228 vtl->route_finder_check_added_track = FALSE;
6229 vtl->route_finder_append = FALSE;
6231 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6233 vtl->route_finder_started = TRUE;
6234 vtl->route_finder_coord = tmp;
6235 vtl->route_finder_current_track = NULL;
6240 /*** Show picture ****/
6242 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6247 /* Params are: vvp, event, last match found or NULL */
6248 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6250 if ( wp->image && wp->visible )
6252 gint x, y, slackx, slacky;
6253 GdkEventButton *event = (GdkEventButton *) params[1];
6255 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6256 slackx = wp->image_width / 2;
6257 slacky = wp->image_height / 2;
6258 if ( x <= event->x + slackx && x >= event->x - slackx
6259 && y <= event->y + slacky && y >= event->y - slacky )
6261 params[2] = wp->image; /* we've found a match. however continue searching
6262 * since we want to find the last match -- that
6263 * is, the match that was drawn last. */
6268 static void trw_layer_show_picture ( gpointer pass_along[6] )
6270 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6272 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
6275 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6276 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6277 g_free ( quoted_file );
6278 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6280 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() );
6281 g_error_free ( err );
6284 #endif /* WINDOWS */
6287 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6289 gpointer params[3] = { vvp, event, NULL };
6290 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6292 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6295 static gpointer pass_along[6];
6296 pass_along[0] = vtl;
6297 pass_along[5] = params[2];
6298 trw_layer_show_picture ( pass_along );
6299 return TRUE; /* found a match */
6302 return FALSE; /* go through other layers, searching for a match */
6305 /***************************************************************************
6307 ***************************************************************************/
6313 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6315 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6316 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6319 /* Structure for thumbnail creating data used in the background thread */
6321 VikTrwLayer *vtl; // Layer needed for redrawing
6322 GSList *pics; // Image list
6323 } thumbnail_create_thread_data;
6325 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6327 guint total = g_slist_length(tctd->pics), done = 0;
6328 while ( tctd->pics )
6330 a_thumbnails_create ( (gchar *) tctd->pics->data );
6331 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6333 return -1; /* Abort thread */
6335 tctd->pics = tctd->pics->next;
6338 // Redraw to show the thumbnails as they are now created
6339 if ( IS_VIK_LAYER(tctd->vtl) )
6340 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6345 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6347 while ( tctd->pics )
6349 g_free ( tctd->pics->data );
6350 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6355 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6357 if ( ! vtl->has_verified_thumbnails )
6359 GSList *pics = NULL;
6360 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6363 gint len = g_slist_length ( pics );
6364 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6365 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6368 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6370 (vik_thr_func) create_thumbnails_thread,
6372 (vik_thr_free_func) thumbnail_create_thread_free,
6380 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6382 return vtl->coord_mode;
6386 * Uniquify the whole layer
6387 * Also requires the layers panel as the names shown there need updating too
6388 * Returns whether the operation was successful or not
6390 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6393 vik_trw_layer_uniquify_tracks ( vtl, vlp );
6394 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
6400 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
6402 vik_coord_convert ( &(wp->coord), *dest_mode );
6405 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6407 vik_track_convert ( tr, *dest_mode );
6410 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6412 if ( vtl->coord_mode != dest_mode )
6414 vtl->coord_mode = dest_mode;
6415 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6416 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6420 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
6422 vtl->menu_selection = selection;
6425 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
6427 return (vtl->menu_selection);
6430 /* ----------- Downloading maps along tracks --------------- */
6432 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6434 /* TODO: calculating based on current size of viewport */
6435 const gdouble w_at_zoom_0_125 = 0.0013;
6436 const gdouble h_at_zoom_0_125 = 0.0011;
6437 gdouble zoom_factor = zoom_level/0.125;
6439 wh->lat = h_at_zoom_0_125 * zoom_factor;
6440 wh->lon = w_at_zoom_0_125 * zoom_factor;
6442 return 0; /* all OK */
6445 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6447 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6448 (dist->lat >= ABS(to->north_south - from->north_south)))
6451 VikCoord *coord = g_malloc(sizeof(VikCoord));
6452 coord->mode = VIK_COORD_LATLON;
6454 if (ABS(gradient) < 1) {
6455 if (from->east_west > to->east_west)
6456 coord->east_west = from->east_west - dist->lon;
6458 coord->east_west = from->east_west + dist->lon;
6459 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
6461 if (from->north_south > to->north_south)
6462 coord->north_south = from->north_south - dist->lat;
6464 coord->north_south = from->north_south + dist->lat;
6465 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
6471 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
6473 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
6474 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
6476 VikCoord *next = from;
6478 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
6480 list = g_list_prepend(list, next);
6486 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
6488 typedef struct _Rect {
6493 #define GLRECT(iter) ((Rect *)((iter)->data))
6496 GList *rects_to_download = NULL;
6499 if (get_download_area_width(vvp, zoom_level, &wh))
6502 GList *iter = tr->trackpoints;
6506 gboolean new_map = TRUE;
6507 VikCoord *cur_coord, tl, br;
6510 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
6512 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6513 rect = g_malloc(sizeof(Rect));
6516 rect->center = *cur_coord;
6517 rects_to_download = g_list_prepend(rects_to_download, rect);
6522 gboolean found = FALSE;
6523 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6524 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
6535 GList *fillins = NULL;
6536 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
6537 /* seems that ATM the function get_next_coord works only for LATLON */
6538 if ( cur_coord->mode == VIK_COORD_LATLON ) {
6539 /* fill-ins for far apart points */
6540 GList *cur_rect, *next_rect;
6541 for (cur_rect = rects_to_download;
6542 (next_rect = cur_rect->next) != NULL;
6543 cur_rect = cur_rect->next) {
6544 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
6545 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
6546 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
6550 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
6553 GList *iter = fillins;
6555 cur_coord = (VikCoord *)(iter->data);
6556 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6557 rect = g_malloc(sizeof(Rect));
6560 rect->center = *cur_coord;
6561 rects_to_download = g_list_prepend(rects_to_download, rect);
6566 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6567 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
6571 for (iter = fillins; iter; iter = iter->next)
6573 g_list_free(fillins);
6575 if (rects_to_download) {
6576 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
6577 g_free(rect_iter->data);
6578 g_list_free(rects_to_download);
6582 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6585 gint selected_map, default_map;
6586 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6587 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6588 gint selected_zoom, default_zoom;
6592 VikTrwLayer *vtl = pass_along[0];
6593 VikLayersPanel *vlp = pass_along[1];
6594 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6595 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6597 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6598 int num_maps = g_list_length(vmls);
6601 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6605 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6606 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6608 gchar **np = map_names;
6609 VikMapsLayer **lp = map_layers;
6610 for (i = 0; i < num_maps; i++) {
6611 gboolean dup = FALSE;
6612 vml = (VikMapsLayer *)(vmls->data);
6613 for (j = 0; j < i; j++) { /* no duplicate allowed */
6614 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6621 *np++ = vik_maps_layer_get_map_label(vml);
6627 num_maps = lp - map_layers;
6629 for (default_map = 0; default_map < num_maps; default_map++) {
6630 /* TODO: check for parent layer's visibility */
6631 if (VIK_LAYER(map_layers[default_map])->visible)
6634 default_map = (default_map == num_maps) ? 0 : default_map;
6636 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6637 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6638 if (cur_zoom == zoom_vals[default_zoom])
6641 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6643 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6646 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6649 for (i = 0; i < num_maps; i++)
6650 g_free(map_names[i]);
6658 /**** lowest waypoint number calculation ***/
6659 static gint highest_wp_number_name_to_number(const gchar *name) {
6660 if ( strlen(name) == 3 ) {
6662 if ( n < 100 && name[0] != '0' )
6664 if ( n < 10 && name[0] != '0' )
6672 static void highest_wp_number_reset(VikTrwLayer *vtl)
6674 vtl->highest_wp_number = -1;
6677 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6679 /* if is bigger that top, add it */
6680 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6681 if ( new_wp_num > vtl->highest_wp_number )
6682 vtl->highest_wp_number = new_wp_num;
6685 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6687 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6688 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6689 if ( vtl->highest_wp_number == old_wp_num ) {
6691 vtl->highest_wp_number--;
6693 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6694 /* search down until we find something that *does* exist */
6696 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
6697 vtl->highest_wp_number--;
6698 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6703 /* get lowest unused number */
6704 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6707 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6709 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6710 return g_strdup(buf);