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 13
85 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
86 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
87 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
88 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
90 #define DRAWMODE_BY_TRACK 0
91 #define DRAWMODE_BY_VELOCITY 1
92 #define DRAWMODE_ALL_BLACK 2
97 /* this is how it knows when you click if you are clicking close to a trackpoint. */
98 #define TRACKPOINT_SIZE_APPROX 5
99 #define WAYPOINT_SIZE_APPROX 5
101 #define MIN_STOP_LENGTH 15
102 #define MAX_STOP_LENGTH 86400
103 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
104 /* this is multiplied by user-inputted value from 1-100. */
106 VIK_TRW_LAYER_SUBLAYER_TRACKS,
107 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
108 VIK_TRW_LAYER_SUBLAYER_TRACK,
109 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
112 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
114 struct _VikTrwLayer {
117 GHashTable *tracks_iters;
118 GHashTable *waypoints_iters;
119 GHashTable *waypoints;
120 GtkTreeIter waypoints_iter, tracks_iter;
121 gboolean tracks_visible, waypoints_visible;
124 guint8 drawelevation;
125 guint8 elevation_factor;
129 guint8 line_thickness;
130 guint8 bg_line_thickness;
134 gboolean wp_draw_symbols;
136 gdouble velocity_min, velocity_max;
138 guint16 track_gc_iter;
139 GdkGC *current_track_gc;
142 GdkGC *waypoint_text_gc;
143 GdkGC *waypoint_bg_gc;
144 GdkFont *waypoint_font;
145 VikTrack *current_track;
146 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
147 gboolean ct_sync_done;
150 VikCoordMode coord_mode;
152 /* wp editing tool */
153 VikWaypoint *current_wp;
154 gpointer current_wp_id;
156 gboolean waypoint_rightclick;
158 /* track editing tool */
160 VikTrack *current_tp_track;
161 gpointer current_tp_id;
162 VikTrwLayerTpwin *tpwin;
164 /* weird hack for joining tracks */
166 VikTrack *last_tp_track;
168 /* track editing tool -- more specifically, moving tps */
171 /* route finder tool */
172 gboolean route_finder_started;
173 VikCoord route_finder_coord;
174 gboolean route_finder_check_added_track;
175 VikTrack *route_finder_added_track;
176 VikTrack *route_finder_current_track;
177 gboolean route_finder_append;
184 guint16 image_cache_size;
186 /* for waypoint text */
187 PangoLayout *wplabellayout;
189 gboolean has_verified_thumbnails;
191 GtkMenu *wp_right_click_menu;
192 GtkMenu *track_right_click_menu;
195 VikStdLayerMenuItem menu_selection;
197 gint highest_wp_number;
200 /* A caached waypoint image. */
203 gchar *image; /* filename */
206 struct DrawingParams {
210 guint16 width, height;
211 const VikCoord *center;
213 gboolean one_zone, lat_lon;
214 gdouble ce1, ce2, cn1, cn2;
217 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
219 static void trw_layer_delete_item ( gpointer pass_along[6] );
220 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
221 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
223 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
224 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
225 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
227 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
228 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
230 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
231 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
232 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
234 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
235 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
236 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
237 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
238 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
239 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
240 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
241 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
242 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
243 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
244 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
245 static void trw_layer_reverse ( gpointer pass_along[6] );
246 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
247 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
248 static void trw_layer_show_picture ( gpointer pass_along[6] );
250 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
251 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
252 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
253 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
254 static void trw_layer_new_wp ( gpointer lav[2] );
255 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
256 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
257 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
258 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
259 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
260 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
261 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
262 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
263 #ifdef VIK_CONFIG_GEOTAG
264 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
265 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
266 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
267 static void trw_layer_geotagging ( gpointer lav[2] );
269 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
270 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
271 #ifdef VIK_CONFIG_OPENSTREETMAP
272 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
274 #ifdef VIK_CONFIG_GEOCACHES
275 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
277 #ifdef VIK_CONFIG_GEOTAG
278 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
282 static void trw_layer_properties_item ( gpointer pass_along[7] );
283 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
284 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
286 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
287 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
288 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
290 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
291 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
292 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
293 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
294 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
296 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
297 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
298 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
299 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
300 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
301 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
302 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
303 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
304 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
305 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
306 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
307 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
308 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
309 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
310 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
311 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
312 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
313 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
314 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
315 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
318 static void cached_pixbuf_free ( CachedPixbuf *cp );
319 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
321 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
322 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
324 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
325 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
326 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
328 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
329 static void highest_wp_number_reset(VikTrwLayer *vtl);
330 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
331 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
334 static VikToolInterface trw_layer_tools[] = {
335 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
336 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
338 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
339 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
340 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
342 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
343 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
345 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
346 (VikToolMouseFunc) tool_edit_waypoint_click,
347 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
348 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
350 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
351 (VikToolMouseFunc) tool_edit_trackpoint_click,
352 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
353 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
355 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
356 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
358 { N_("Route Finder"), (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
359 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
361 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
363 /****** PARAMETERS ******/
365 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
366 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
368 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
369 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
372 static VikLayerParamScale params_scales[] = {
373 /* min max step digits */
374 { 1, 10, 1, 0 }, /* line_thickness */
375 { 0.0, 99.0, 1, 2 }, /* velocity_min */
376 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
377 /* 5 * step == how much to turn */
378 { 16, 128, 3.2, 0 }, /* image_size */
379 { 0, 255, 5, 0 }, /* image alpha */
380 { 5, 500, 5, 0 }, /* image cache_size */
381 { 0, 8, 1, 0 }, /* image cache_size */
382 { 1, 64, 1, 0 }, /* wpsize */
383 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
384 { 1, 100, 1, 0 }, /* stop_length */
387 VikLayerParam trw_layer_params[] = {
388 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
389 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
391 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
392 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
393 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
394 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
395 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
397 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
398 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
400 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
401 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
402 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
403 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
404 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
406 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
407 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
408 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
409 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
410 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
411 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
412 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
413 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
415 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
416 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
417 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
418 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
421 enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_DE, PARAM_EF, PARAM_DS, PARAM_SL, PARAM_LT, PARAM_BLT, PARAM_TBGC, PARAM_VMIN, PARAM_VMAX, PARAM_DLA, PARAM_WPC, PARAM_WPTC, PARAM_WPBC, PARAM_WPBA, PARAM_WPSYM, PARAM_WPSIZE, PARAM_WPSYMS, PARAM_DI, PARAM_IS, PARAM_IA, PARAM_ICS, NUM_PARAMS };
424 *** 1) Add to trw_layer_params and enumeration
425 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
428 /****** END PARAMETERS ******/
430 static VikTrwLayer* trw_layer_new ( gint drawmode );
431 /* Layer Interface function definitions */
432 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
433 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
434 static void trw_layer_free ( VikTrwLayer *trwlayer );
435 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
436 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
437 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
438 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
439 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
440 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
441 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
442 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
443 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
444 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
445 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
446 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
447 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
448 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
449 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
450 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
451 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
452 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
453 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
454 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
455 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
456 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
457 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
458 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
459 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
460 /* End Layer Interface function definitions */
462 VikLayerInterface vik_trw_layer_interface = {
467 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
471 params_groups, /* params_groups */
472 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
476 (VikLayerFuncCreate) trw_layer_create,
477 (VikLayerFuncRealize) trw_layer_realize,
478 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
479 (VikLayerFuncFree) trw_layer_free,
481 (VikLayerFuncProperties) NULL,
482 (VikLayerFuncDraw) trw_layer_draw,
483 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
485 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
486 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
488 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
489 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
491 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
492 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
493 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
494 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
495 (VikLayerFuncLayerSelected) trw_layer_selected,
497 (VikLayerFuncMarshall) trw_layer_marshall,
498 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
500 (VikLayerFuncSetParam) trw_layer_set_param,
501 (VikLayerFuncGetParam) trw_layer_get_param,
503 (VikLayerFuncReadFileData) a_gpspoint_read_file,
504 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
506 (VikLayerFuncDeleteItem) trw_layer_del_item,
507 (VikLayerFuncCutItem) trw_layer_cut_item,
508 (VikLayerFuncCopyItem) trw_layer_copy_item,
509 (VikLayerFuncPasteItem) trw_layer_paste_item,
510 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
512 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
514 (VikLayerFuncSelectClick) trw_layer_select_click,
515 (VikLayerFuncSelectMove) trw_layer_select_move,
516 (VikLayerFuncSelectRelease) trw_layer_select_release,
517 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
526 GType vik_trw_layer_get_type ()
528 static GType vtl_type = 0;
532 static const GTypeInfo vtl_info =
534 sizeof (VikTrwLayerClass),
535 NULL, /* base_init */
536 NULL, /* base_finalize */
537 NULL, /* class init */
538 NULL, /* class_finalize */
539 NULL, /* class_data */
540 sizeof (VikTrwLayer),
542 NULL /* instance init */
544 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
550 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
552 static gpointer pass_along[6];
558 pass_along[1] = NULL;
559 pass_along[2] = GINT_TO_POINTER (subtype);
560 pass_along[3] = sublayer;
561 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
562 pass_along[5] = NULL;
564 trw_layer_delete_item ( pass_along );
567 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
569 static gpointer pass_along[6];
575 pass_along[1] = NULL;
576 pass_along[2] = GINT_TO_POINTER (subtype);
577 pass_along[3] = sublayer;
578 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
579 pass_along[5] = NULL;
581 trw_layer_copy_item_cb(pass_along);
582 trw_layer_cut_item_cb(pass_along);
585 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
587 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
588 gint subtype = GPOINTER_TO_INT (pass_along[2]);
589 gpointer * sublayer = pass_along[3];
593 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
597 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
598 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
599 if ( wp && wp->name )
602 name = NULL; // Broken :(
605 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
606 if ( trk && trk->name )
609 name = NULL; // Broken :(
612 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
613 subtype, len, name, data);
617 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
619 trw_layer_copy_item_cb(pass_along);
620 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
621 trw_layer_delete_item(pass_along);
624 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
635 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
637 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
638 // 'Simple' memory copy of byte array from the marshalling above
639 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
640 fi = g_malloc ( *len );
642 memcpy(fi->data, id, il);
645 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
646 // less magic than before...
647 *len = sizeof(FlatItem) + 1 + il;
648 fi = g_malloc ( *len );
650 memcpy(fi->data, id, il);
654 *item = (guint8 *)fi;
657 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
659 FlatItem *fi = (FlatItem *) item;
661 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
666 w = vik_waypoint_unmarshall(fi->data, fi->len);
667 // When copying - we'll create a new name based on the original
668 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
669 vik_trw_layer_add_waypoint ( vtl, name, w );
670 waypoint_convert (NULL, w, &vtl->coord_mode);
672 // Consider if redraw necessary for the new item
673 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
674 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
677 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
682 t = vik_track_unmarshall(fi->data, fi->len);
683 // When copying - we'll create a new name based on the original
684 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
685 vik_trw_layer_add_track ( vtl, name, t );
686 track_convert (name, t, &vtl->coord_mode);
688 // Consider if redraw necessary for the new item
689 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
690 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
696 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
703 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
707 case PARAM_TV: vtl->tracks_visible = data.b; break;
708 case PARAM_WV: vtl->waypoints_visible = data.b; break;
709 case PARAM_DM: vtl->drawmode = data.u; break;
710 case PARAM_DP: vtl->drawpoints = data.b; break;
711 case PARAM_DE: vtl->drawelevation = data.b; break;
712 case PARAM_DS: vtl->drawstops = data.b; break;
713 case PARAM_DL: vtl->drawlines = data.b; break;
714 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
715 vtl->stop_length = data.u;
717 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
718 vtl->elevation_factor = data.u;
720 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
722 vtl->line_thickness = data.u;
723 trw_layer_new_track_gcs ( vtl, vp );
726 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
728 vtl->bg_line_thickness = data.u;
729 trw_layer_new_track_gcs ( vtl, vp );
734 /* Convert to store internally
735 NB file operation always in internal units (metres per second) */
736 vik_units_speed_t speed_units = a_vik_get_units_speed ();
737 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
738 vtl->velocity_min = data.d;
739 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
740 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
741 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
742 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
745 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
750 /* Convert to store internally
751 NB file operation always in internal units (metres per second) */
752 vik_units_speed_t speed_units = a_vik_get_units_speed ();
753 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
754 vtl->velocity_max = data.d;
755 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
756 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
757 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
758 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
761 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
764 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
765 case PARAM_DLA: vtl->drawlabels = data.b; break;
766 case PARAM_DI: vtl->drawimages = data.b; break;
767 case PARAM_IS: if ( data.u != vtl->image_size )
769 vtl->image_size = data.u;
770 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
771 g_queue_free ( vtl->image_cache );
772 vtl->image_cache = g_queue_new ();
775 case PARAM_IA: vtl->image_alpha = data.u; break;
776 case PARAM_ICS: vtl->image_cache_size = data.u;
777 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
778 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
780 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
781 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
782 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
783 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
784 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
785 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
786 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
791 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
793 VikLayerParamData rv;
796 case PARAM_TV: rv.b = vtl->tracks_visible; break;
797 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
798 case PARAM_DM: rv.u = vtl->drawmode; break;
799 case PARAM_DP: rv.b = vtl->drawpoints; break;
800 case PARAM_DE: rv.b = vtl->drawelevation; break;
801 case PARAM_EF: rv.u = vtl->elevation_factor; break;
802 case PARAM_DS: rv.b = vtl->drawstops; break;
803 case PARAM_SL: rv.u = vtl->stop_length; break;
804 case PARAM_DL: rv.b = vtl->drawlines; break;
805 case PARAM_LT: rv.u = vtl->line_thickness; break;
806 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
809 /* Convert to store internally
810 NB file operation always in internal units (metres per second) */
811 vik_units_speed_t speed_units = a_vik_get_units_speed ();
812 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
813 rv.d = vtl->velocity_min;
814 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
815 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
816 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
817 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
820 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
825 /* Convert to store internally
826 NB file operation always in internal units (metres per second) */
827 vik_units_speed_t speed_units = a_vik_get_units_speed ();
828 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
829 rv.d = vtl->velocity_max;
830 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
831 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
832 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
833 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
836 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
839 case PARAM_DLA: rv.b = vtl->drawlabels; break;
840 case PARAM_DI: rv.b = vtl->drawimages; break;
841 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
842 case PARAM_IS: rv.u = vtl->image_size; break;
843 case PARAM_IA: rv.u = vtl->image_alpha; break;
844 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
845 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
846 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
847 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
848 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
849 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
850 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
851 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
856 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
867 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
868 a_gpx_write_file(vtl, f);
869 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
872 g_file_get_contents(tmpname, &dd, &dl, NULL);
873 *len = sizeof(pl) + pl + dl;
874 *data = g_malloc(*len);
875 memcpy(*data, &pl, sizeof(pl));
876 memcpy(*data + sizeof(pl), pd, pl);
877 memcpy(*data + sizeof(pl) + pl, dd, dl);
886 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
888 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
894 memcpy(&pl, data, sizeof(pl));
896 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
899 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
900 g_critical("couldn't open temp file");
903 fwrite(data, len - pl - sizeof(pl), 1, f);
905 a_gpx_read_file(rv, f);
913 static GList * str_array_to_glist(gchar* data[])
917 for (p = (gpointer)data; *p; p++)
918 gl = g_list_prepend(gl, *p);
919 return(g_list_reverse(gl));
922 // Keep interesting hash function at least visible
924 static guint strcase_hash(gconstpointer v)
926 // 31 bit hash function
929 gchar s[128]; // malloc is too slow for reading big files
932 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
933 p[i] = toupper(t[i]);
939 for (p += 1; *p != '\0'; p++)
940 h = (h << 5) - h + *p;
947 static VikTrwLayer* trw_layer_new ( gint drawmode )
949 if (trw_layer_params[PARAM_DM].widget_data == NULL)
950 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
951 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
952 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
954 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
955 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
957 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
958 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
960 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
961 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
962 // and with normal PC processing capabilities - it has negligibile performance impact
963 // This also minimized the amount of rework - as the management of the hash tables already exists.
965 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
966 // we have to maintain a uniqueness (as before when multiple names where not allowed),
967 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
969 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
970 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
971 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
972 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
974 /* TODO: constants at top */
975 rv->waypoints_visible = rv->tracks_visible = TRUE;
976 rv->drawmode = drawmode;
977 rv->drawpoints = TRUE;
978 rv->drawstops = FALSE;
979 rv->drawelevation = FALSE;
980 rv->elevation_factor = 30;
981 rv->stop_length = 60;
982 rv->drawlines = TRUE;
983 rv->wplabellayout = NULL;
984 rv->wp_right_click_menu = NULL;
985 rv->track_right_click_menu = NULL;
986 rv->waypoint_gc = NULL;
987 rv->waypoint_text_gc = NULL;
988 rv->waypoint_bg_gc = NULL;
990 rv->velocity_max = 5.0;
991 rv->velocity_min = 0.0;
992 rv->line_thickness = 1;
993 rv->bg_line_thickness = 0;
994 rv->current_wp = NULL;
995 rv->current_wp_id = NULL;
996 rv->current_track = NULL;
997 rv->current_tpl = NULL;
998 rv->current_tp_track = NULL;
999 rv->current_tp_id = NULL;
1000 rv->moving_tp = FALSE;
1001 rv->moving_wp = FALSE;
1003 rv->ct_sync_done = TRUE;
1005 rv->route_finder_started = FALSE;
1006 rv->route_finder_check_added_track = FALSE;
1007 rv->route_finder_current_track = NULL;
1008 rv->route_finder_append = FALSE;
1010 rv->waypoint_rightclick = FALSE;
1011 rv->last_tpl = NULL;
1012 rv->last_tp_track = NULL;
1014 rv->image_cache = g_queue_new();
1015 rv->image_size = 64;
1016 rv->image_alpha = 255;
1017 rv->image_cache_size = 300;
1018 rv->drawimages = TRUE;
1019 rv->drawlabels = TRUE;
1024 static void trw_layer_free ( VikTrwLayer *trwlayer )
1026 g_hash_table_destroy(trwlayer->waypoints);
1027 g_hash_table_destroy(trwlayer->tracks);
1029 /* ODC: replace with GArray */
1030 trw_layer_free_track_gcs ( trwlayer );
1032 if ( trwlayer->wp_right_click_menu )
1033 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1035 if ( trwlayer->track_right_click_menu )
1036 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1038 if ( trwlayer->wplabellayout != NULL)
1039 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1041 if ( trwlayer->waypoint_gc != NULL )
1042 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1044 if ( trwlayer->waypoint_text_gc != NULL )
1045 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1047 if ( trwlayer->waypoint_bg_gc != NULL )
1048 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1050 if ( trwlayer->waypoint_font != NULL )
1051 gdk_font_unref ( trwlayer->waypoint_font );
1053 if ( trwlayer->tpwin != NULL )
1054 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1056 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1057 g_queue_free ( trwlayer->image_cache );
1060 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
1063 dp->xmpp = vik_viewport_get_xmpp ( vp );
1064 dp->ympp = vik_viewport_get_ympp ( vp );
1065 dp->width = vik_viewport_get_width ( vp );
1066 dp->height = vik_viewport_get_height ( vp );
1067 dp->center = vik_viewport_get_center ( vp );
1068 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1069 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1074 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1075 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1076 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1078 dp->ce1 = dp->center->east_west-w2;
1079 dp->ce2 = dp->center->east_west+w2;
1080 dp->cn1 = dp->center->north_south-h2;
1081 dp->cn2 = dp->center->north_south+h2;
1082 } else if ( dp->lat_lon ) {
1083 VikCoord upperleft, bottomright;
1084 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1085 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1086 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1087 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1088 dp->ce1 = upperleft.east_west;
1089 dp->ce2 = bottomright.east_west;
1090 dp->cn1 = bottomright.north_south;
1091 dp->cn2 = upperleft.north_south;
1094 dp->track_gc_iter = 0;
1097 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1099 static gdouble rv = 0;
1100 if ( tp1->has_timestamp && tp2->has_timestamp )
1102 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1103 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1106 return VIK_TRW_LAYER_TRACK_GC_MIN;
1107 else if ( vtl->velocity_min >= vtl->velocity_max )
1108 return VIK_TRW_LAYER_TRACK_GC_MAX;
1110 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1112 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1113 return VIK_TRW_LAYER_TRACK_GC_MAX;
1117 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1120 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1122 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1123 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1124 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1125 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1128 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1130 /* TODO: this function is a mess, get rid of any redundancy */
1131 GList *list = track->trackpoints;
1133 gboolean useoldvals = TRUE;
1135 gboolean drawpoints;
1137 gboolean drawelevation;
1138 gdouble min_alt, max_alt, alt_diff = 0;
1140 const guint8 tp_size_reg = 2;
1141 const guint8 tp_size_cur = 4;
1144 if ( dp->vtl->drawelevation )
1146 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1147 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1148 alt_diff = max_alt - min_alt;
1151 if ( ! track->visible )
1154 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1155 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1156 trw_layer_draw_track ( name, track, dp, TRUE );
1158 if ( drawing_white_background )
1159 drawpoints = drawstops = FALSE;
1161 drawpoints = dp->vtl->drawpoints;
1162 drawstops = dp->vtl->drawstops;
1165 /* Current track - used for creation */
1166 if ( track == dp->vtl->current_track )
1167 main_gc = dp->vtl->current_track_gc;
1169 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1170 /* Draw all tracks of the layer in special colour */
1171 /* if track is member of selected layer or is the current selected track
1172 then draw in the highlight colour.
1173 NB this supercedes the drawmode */
1174 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1175 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1176 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1177 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1180 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1181 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1183 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1187 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1188 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1190 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1195 int x, y, oldx, oldy;
1196 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1198 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1200 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1202 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1204 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1205 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1211 while ((list = g_list_next(list)))
1213 tp = VIK_TRACKPOINT(list->data);
1214 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1216 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1217 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1218 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1219 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1220 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1222 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1225 * If points are the same in display coordinates, don't draw.
1227 if ( useoldvals && x == oldx && y == oldy )
1229 // Still need to process points to ensure 'stops' are drawn if required
1230 if ( drawstops && drawpoints && ! drawing_white_background && list->next &&
1231 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1232 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 );
1237 if ( drawpoints && ! drawing_white_background )
1241 * The concept of drawing stops is that a trackpoint
1242 * that is if the next trackpoint has a timestamp far into
1243 * the future, we draw a circle of 6x trackpoint size,
1244 * instead of a rectangle of 2x trackpoint size.
1245 * This is drawn first so the trackpoint will be drawn on top
1248 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1249 /* Stop point. Draw 6x circle. */
1250 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 );
1252 /* Regular point - draw 2x square. */
1253 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1256 /* Final point - draw 4x circle. */
1257 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 );
1260 if ((!tp->newsegment) && (dp->vtl->drawlines))
1262 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1264 /* UTM only: zone check */
1265 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1266 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1268 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1269 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1270 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1274 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1276 if ( drawing_white_background ) {
1277 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1281 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1282 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1284 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1289 tmp[1].y = oldy-FIXALTITUDE(list->data);
1291 tmp[2].y = y-FIXALTITUDE(list->next->data);
1296 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1297 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1299 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1300 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1302 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1313 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1315 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1316 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1318 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1319 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1320 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1321 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1325 * If points are the same in display coordinates, don't draw.
1327 if ( x != oldx || y != oldy )
1329 if ( drawing_white_background )
1330 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1332 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1338 * If points are the same in display coordinates, don't draw.
1340 if ( x != oldx && y != oldy )
1342 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1343 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1351 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1352 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1353 dp->track_gc_iter = 0;
1356 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1357 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1359 trw_layer_draw_track ( name, track, dp, FALSE );
1362 static void cached_pixbuf_free ( CachedPixbuf *cp )
1364 g_object_unref ( G_OBJECT(cp->pixbuf) );
1365 g_free ( cp->image );
1368 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1370 return strcmp ( cp->image, name );
1373 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1376 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1377 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1378 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1381 GdkPixbuf *sym = NULL;
1382 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1384 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1386 if ( wp->image && dp->vtl->drawimages )
1388 GdkPixbuf *pixbuf = NULL;
1391 if ( dp->vtl->image_alpha == 0)
1394 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1396 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1399 gchar *image = wp->image;
1400 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1401 if ( ! regularthumb )
1403 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1404 image = "\x12\x00"; /* this shouldn't occur naturally. */
1408 CachedPixbuf *cp = NULL;
1409 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1410 if ( dp->vtl->image_size == 128 )
1411 cp->pixbuf = regularthumb;
1414 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1415 g_assert ( cp->pixbuf );
1416 g_object_unref ( G_OBJECT(regularthumb) );
1418 cp->image = g_strdup ( image );
1420 /* needed so 'click picture' tool knows how big the pic is; we don't
1421 * store it in cp because they may have been freed already. */
1422 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1423 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1425 g_queue_push_head ( dp->vtl->image_cache, cp );
1426 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1427 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1429 pixbuf = cp->pixbuf;
1433 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1439 w = gdk_pixbuf_get_width ( pixbuf );
1440 h = gdk_pixbuf_get_height ( pixbuf );
1442 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1444 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1445 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1446 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1447 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1448 // Highlighted - so draw a little border around the chosen one
1449 // single line seems a little weak so draw 2 of them
1450 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1451 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1452 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1453 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1456 if ( dp->vtl->image_alpha == 255 )
1457 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1459 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1461 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1465 /* DRAW ACTUAL DOT */
1466 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1467 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 );
1469 else if ( wp == dp->vtl->current_wp ) {
1470 switch ( dp->vtl->wp_symbol ) {
1471 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;
1472 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;
1473 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;
1474 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 );
1475 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 );
1479 switch ( dp->vtl->wp_symbol ) {
1480 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;
1481 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;
1482 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;
1483 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 );
1484 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;
1488 if ( dp->vtl->drawlabels )
1490 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1491 gint label_x, label_y;
1493 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1494 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1495 label_x = x - width/2;
1497 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1499 label_y = y - dp->vtl->wp_size - height - 2;
1501 /* if highlight mode on, then draw background text in highlight colour */
1502 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1503 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1504 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1505 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1506 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1508 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1511 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1513 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1518 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1520 static struct DrawingParams dp;
1521 g_assert ( l != NULL );
1523 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1526 if ( l->tracks_visible )
1527 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1529 if (l->waypoints_visible)
1530 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1533 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1536 if ( vtl->track_bg_gc )
1538 g_object_unref ( vtl->track_bg_gc );
1539 vtl->track_bg_gc = NULL;
1541 if ( vtl->current_track_gc )
1543 g_object_unref ( vtl->current_track_gc );
1544 vtl->current_track_gc = NULL;
1547 if ( ! vtl->track_gc )
1549 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1550 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1551 g_array_free ( vtl->track_gc, TRUE );
1552 vtl->track_gc = NULL;
1555 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1557 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1558 gint width = vtl->line_thickness;
1560 if ( vtl->track_gc )
1561 trw_layer_free_track_gcs ( vtl );
1563 if ( vtl->track_bg_gc )
1564 g_object_unref ( vtl->track_bg_gc );
1565 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1567 if ( vtl->current_track_gc )
1568 g_object_unref ( vtl->current_track_gc );
1569 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1570 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1572 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1574 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1576 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1577 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1578 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1579 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1580 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1581 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1582 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1583 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1584 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1585 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1587 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1589 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1591 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1594 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1596 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1597 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1599 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1600 /* early exit, as the rest is GUI related */
1604 PangoFontDescription *pfd;
1605 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1606 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1607 pango_layout_set_font_description (rv->wplabellayout, pfd);
1608 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1609 pango_font_description_free (pfd);
1611 trw_layer_new_track_gcs ( rv, vp );
1613 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1614 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1615 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1616 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1618 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1620 rv->has_verified_thumbnails = FALSE;
1621 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1623 rv->wp_draw_symbols = TRUE;
1625 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1627 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1632 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1634 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1636 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1637 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 );
1639 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 );
1642 *new_iter = *((GtkTreeIter *) pass_along[1]);
1643 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1645 if ( ! track->visible )
1646 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1649 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1651 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1652 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1653 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 );
1655 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 );
1658 *new_iter = *((GtkTreeIter *) pass_along[1]);
1659 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1661 if ( ! wp->visible )
1662 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1666 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1669 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1671 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1672 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1674 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1676 if ( ! vtl->tracks_visible )
1677 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1679 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1681 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1682 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1684 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1687 if ( ! vtl->waypoints_visible )
1688 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1690 pass_along[0] = &(vtl->waypoints_iter);
1691 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1693 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1697 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1701 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1702 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1703 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1705 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1707 return (t->visible ^= 1);
1711 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1713 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1715 return (t->visible ^= 1);
1724 * Return a property about tracks for this layer
1726 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1728 return vtl->line_thickness;
1731 // Structure to hold multiple track information for a layer
1740 * Build up layer multiple track information via updating the tooltip_tracks structure
1742 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1744 tt->length = tt->length + vik_track_get_length (tr);
1746 // Ensure times are available
1747 if ( tr->trackpoints &&
1748 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1749 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1752 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1753 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1755 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1756 // Hence initialize to the first 'proper' value
1757 if ( tt->start_time == 0 )
1758 tt->start_time = t1;
1759 if ( tt->end_time == 0 )
1762 // Update find the earliest / last times
1763 if ( t1 < tt->start_time )
1764 tt->start_time = t1;
1765 if ( t2 > tt->end_time )
1768 // Keep track of total time
1769 // there maybe gaps within a track (eg segments)
1770 // but this should be generally good enough for a simple indicator
1771 tt->duration = tt->duration + (int)(t2-t1);
1776 * Generate tooltip text for the layer.
1777 * This is relatively complicated as it considers information for
1778 * no tracks, a single track or multiple tracks
1779 * (which may or may not have timing information)
1781 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1792 static gchar tmp_buf[128];
1795 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1797 // Safety check - I think these should always be valid
1798 if ( vtl->tracks && vtl->waypoints ) {
1799 tooltip_tracks tt = { 0.0, 0, 0 };
1800 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1802 GDate* gdate_start = g_date_new ();
1803 g_date_set_time_t (gdate_start, tt.start_time);
1805 GDate* gdate_end = g_date_new ();
1806 g_date_set_time_t (gdate_end, tt.end_time);
1808 if ( g_date_compare (gdate_start, gdate_end) ) {
1809 // Dates differ so print range on separate line
1810 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1811 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1812 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1815 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1816 if ( tt.start_time != 0 )
1817 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1821 if ( tt.length > 0.0 ) {
1822 gdouble len_in_units;
1824 // Setup info dependent on distance units
1825 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1826 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1827 len_in_units = VIK_METERS_TO_MILES(tt.length);
1830 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1831 len_in_units = tt.length/1000.0;
1834 // Timing information if available
1836 if ( tt.duration > 0 ) {
1837 g_snprintf (tbuf1, sizeof(tbuf1),
1838 _(" in %d:%02d hrs:mins"),
1839 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1841 g_snprintf (tbuf2, sizeof(tbuf2),
1842 _("\n%sTotal Length %.1f %s%s"),
1843 tbuf3, len_in_units, tbuf4, tbuf1);
1846 // Put together all the elements to form compact tooltip text
1847 g_snprintf (tmp_buf, sizeof(tmp_buf),
1848 _("Tracks: %d - Waypoints: %d%s"),
1849 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1851 g_date_free (gdate_start);
1852 g_date_free (gdate_end);
1859 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1863 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1864 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1865 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1867 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1869 // Could be a better way of handling strings - but this works...
1870 gchar time_buf1[20];
1871 gchar time_buf2[20];
1872 time_buf1[0] = '\0';
1873 time_buf2[0] = '\0';
1874 static gchar tmp_buf[100];
1875 // Compact info: Short date eg (11/20/99), duration and length
1876 // Hopefully these are the things that are most useful and so promoted into the tooltip
1877 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1878 // %x The preferred date representation for the current locale without the time.
1879 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1880 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1881 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1883 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1886 // Get length and consider the appropriate distance units
1887 gdouble tr_len = vik_track_get_length(tr);
1888 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1889 switch (dist_units) {
1890 case VIK_UNITS_DISTANCE_KILOMETRES:
1891 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1893 case VIK_UNITS_DISTANCE_MILES:
1894 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1903 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1905 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1906 // NB It's OK to return NULL
1917 * Function to show basic track point information on the statusbar
1919 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1922 switch (a_vik_get_units_height ()) {
1923 case VIK_UNITS_HEIGHT_FEET:
1924 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1927 //VIK_UNITS_HEIGHT_METRES:
1928 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1933 if ( trkpt->has_timestamp ) {
1934 // Compact date time format
1935 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1939 // Position is put later on, as this bit may not be seen if the display is not big enough,
1940 // one can easily use the current pointer position to see this if needed
1941 gchar *lat = NULL, *lon = NULL;
1942 static struct LatLon ll;
1943 vik_coord_to_latlon (&(trkpt->coord), &ll);
1944 a_coords_latlon_to_string ( &ll, &lat, &lon );
1947 // Again is put later on, as this bit may not be seen if the display is not big enough
1948 // trackname can be seen from the treeview (when enabled)
1949 // Also name could be very long to not leave room for anything else
1952 if ( vtl->current_tp_track ) {
1953 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
1956 // Combine parts to make overall message
1957 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
1958 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1965 * Function to show basic waypoint information on the statusbar
1967 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
1970 switch (a_vik_get_units_height ()) {
1971 case VIK_UNITS_HEIGHT_FEET:
1972 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
1975 //VIK_UNITS_HEIGHT_METRES:
1976 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
1980 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
1981 // one can easily use the current pointer position to see this if needed
1982 gchar *lat = NULL, *lon = NULL;
1983 static struct LatLon ll;
1984 vik_coord_to_latlon (&(wpt->coord), &ll);
1985 a_coords_latlon_to_string ( &ll, &lat, &lon );
1987 // Combine parts to make overall message
1990 // Add comment if available
1991 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
1993 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
1994 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2001 * General layer selection function, find out which bit is selected and take appropriate action
2003 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2006 l->current_wp = NULL;
2007 l->current_wp_id = NULL;
2008 trw_layer_cancel_current_tp ( l, FALSE );
2011 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2015 case VIK_TREEVIEW_TYPE_LAYER:
2017 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2018 /* Mark for redraw */
2023 case VIK_TREEVIEW_TYPE_SUBLAYER:
2027 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2029 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2030 /* Mark for redraw */
2034 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2036 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2037 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
2038 /* Mark for redraw */
2042 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2044 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2045 /* Mark for redraw */
2049 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2051 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2053 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
2054 // Show some waypoint info
2055 set_statusbar_msg_info_wpt ( l, wpt );
2056 /* Mark for redraw */
2063 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2072 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2077 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2082 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2084 return l->waypoints;
2088 * ATM use a case sensitive find
2089 * Finds the first one
2091 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2093 if ( wp && wp->name )
2094 if ( ! strcmp ( wp->name, name ) )
2100 * Get waypoint by name - not guaranteed to be unique
2101 * Finds the first one
2103 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2105 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2109 * ATM use a case sensitive find
2110 * Finds the first one
2112 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2114 if ( trk && trk->name )
2115 if ( ! strcmp ( trk->name, name ) )
2121 * Get track by name - not guaranteed to be unique
2122 * Finds the first one
2124 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2126 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2129 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2131 static VikCoord fixme;
2132 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2133 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2134 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2135 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2136 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2137 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2138 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2139 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2140 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2143 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
2146 static VikCoord fixme;
2150 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2151 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2152 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2153 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2154 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2155 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2156 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2157 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2158 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2163 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2165 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2166 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2168 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2169 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2170 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2171 maxmin[0].lat = wpt_maxmin[0].lat;
2174 maxmin[0].lat = trk_maxmin[0].lat;
2176 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2177 maxmin[0].lon = wpt_maxmin[0].lon;
2180 maxmin[0].lon = trk_maxmin[0].lon;
2182 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2183 maxmin[1].lat = wpt_maxmin[1].lat;
2186 maxmin[1].lat = trk_maxmin[1].lat;
2188 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2189 maxmin[1].lon = wpt_maxmin[1].lon;
2192 maxmin[1].lon = trk_maxmin[1].lon;
2196 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2198 /* 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... */
2199 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2200 trw_layer_find_maxmin (vtl, maxmin);
2201 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2205 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2206 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2211 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2214 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2215 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2217 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2220 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2222 /* First set the center [in case previously viewing from elsewhere] */
2223 /* Then loop through zoom levels until provided positions are in view */
2224 /* This method is not particularly fast - but should work well enough */
2225 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2227 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2228 vik_viewport_set_center_coord ( vvp, &coord );
2230 /* Convert into definite 'smallest' and 'largest' positions */
2231 struct LatLon minmin;
2232 if ( maxmin[0].lat < maxmin[1].lat )
2233 minmin.lat = maxmin[0].lat;
2235 minmin.lat = maxmin[1].lat;
2237 struct LatLon maxmax;
2238 if ( maxmin[0].lon > maxmin[1].lon )
2239 maxmax.lon = maxmin[0].lon;
2241 maxmax.lon = maxmin[1].lon;
2243 /* Never zoom in too far - generally not that useful, as too close ! */
2244 /* Always recalculate the 'best' zoom level */
2246 vik_viewport_set_zoom ( vvp, zoom );
2248 gdouble min_lat, max_lat, min_lon, max_lon;
2249 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2250 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2251 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2252 /* NB I think the logic used in this test to determine if the bounds is within view
2253 fails if track goes across 180 degrees longitude.
2254 Hopefully that situation is not too common...
2255 Mind you viking doesn't really do edge locations to well anyway */
2256 if ( min_lat < minmin.lat &&
2257 max_lat > minmin.lat &&
2258 min_lon < maxmax.lon &&
2259 max_lon > maxmax.lon )
2260 /* Found within zoom level */
2265 vik_viewport_set_zoom ( vvp, zoom );
2269 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2271 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2272 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2273 trw_layer_find_maxmin (vtl, maxmin);
2274 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2277 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2282 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2284 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])) ) ) {
2285 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2288 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2291 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2293 GtkWidget *file_selector;
2295 gboolean failed = FALSE;
2296 file_selector = gtk_file_chooser_dialog_new (title,
2298 GTK_FILE_CHOOSER_ACTION_SAVE,
2299 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2300 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2302 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2304 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2306 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2307 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2309 gtk_widget_hide ( file_selector );
2310 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk );
2315 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2317 gtk_widget_hide ( file_selector );
2318 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk );
2323 gtk_widget_destroy ( file_selector );
2325 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2328 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2330 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2333 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2335 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2338 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2340 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2341 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2342 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2343 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2345 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2347 g_free ( auto_save_name );
2350 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2352 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2353 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2354 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2355 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2357 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2359 g_free ( auto_save_name );
2363 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2366 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2368 gchar *name_used = NULL;
2371 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2372 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL);
2374 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2378 gchar *quoted_file = g_shell_quote ( name_used );
2379 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2380 g_free ( quoted_file );
2381 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2383 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2384 g_error_free ( err );
2388 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2389 //g_remove ( name_used );
2390 // Perhaps should be deleted when the program ends?
2391 // For now leave it to the user to delete it / use system temp cleanup methods.
2392 g_free ( name_used );
2396 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2398 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2401 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2403 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2406 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2408 gpointer layer_and_vlp[2];
2409 layer_and_vlp[0] = pass_along[0];
2410 layer_and_vlp[1] = pass_along[1];
2411 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2413 if ( !trk || !trk->name )
2416 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2417 gchar *auto_save_name = g_strdup ( trk->name );
2418 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2419 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2421 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2423 g_free ( auto_save_name );
2427 VikWaypoint *wp; // input
2428 gpointer uuid; // output
2431 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2433 wpu_udata *user_data = udata;
2434 if ( wp == user_data->wp ) {
2435 user_data->uuid = id;
2441 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2443 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2444 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2445 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2447 GTK_RESPONSE_REJECT,
2449 GTK_RESPONSE_ACCEPT,
2452 GtkWidget *label, *entry;
2453 label = gtk_label_new(_("Waypoint Name:"));
2454 entry = gtk_entry_new();
2456 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2457 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2458 gtk_widget_show_all ( label );
2459 gtk_widget_show_all ( entry );
2461 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2463 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2465 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2466 // Find *first* wp with the given name
2467 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2470 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2473 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2474 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2476 // Find and select on the side panel
2481 // Hmmm, want key of it
2482 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2484 if ( wpf && udata.uuid ) {
2485 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2486 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2495 gtk_widget_destroy ( dia );
2498 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2500 gchar *default_name = highest_wp_number_get(vtl);
2501 VikWaypoint *wp = vik_waypoint_new();
2502 gchar *returned_name;
2504 wp->coord = *def_coord;
2506 // Attempt to auto set height if DEM data is available
2507 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2508 if ( elev != VIK_DEM_INVALID_ELEVATION )
2509 wp->altitude = (gdouble)elev;
2511 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2513 if ( returned_name )
2516 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2517 g_free (default_name);
2518 g_free (returned_name);
2521 g_free (default_name);
2522 vik_waypoint_free(wp);
2526 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2529 struct LatLon one_ll, two_ll;
2530 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2532 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2533 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2534 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2535 VikViewport *vvp = vik_window_viewport(vw);
2536 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2537 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2538 vik_coord_to_latlon(&one, &one_ll);
2539 vik_coord_to_latlon(&two, &two_ll);
2540 if (one_ll.lat > two_ll.lat) {
2541 maxmin[0].lat = one_ll.lat;
2542 maxmin[1].lat = two_ll.lat;
2545 maxmin[0].lat = two_ll.lat;
2546 maxmin[1].lat = one_ll.lat;
2548 if (one_ll.lon > two_ll.lon) {
2549 maxmin[0].lon = one_ll.lon;
2550 maxmin[1].lon = two_ll.lon;
2553 maxmin[0].lon = two_ll.lon;
2554 maxmin[1].lon = one_ll.lon;
2556 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2559 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2561 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2562 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2563 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2565 trw_layer_find_maxmin (vtl, maxmin);
2566 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2569 #ifdef VIK_CONFIG_GEOTAG
2570 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2572 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2574 // Update directly - not changing the mtime
2575 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2578 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2580 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2583 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2587 * Use code in separate file for this feature as reasonably complex
2589 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2591 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2592 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2593 // Unset so can be reverified later if necessary
2594 vtl->has_verified_thumbnails = FALSE;
2596 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2602 static void trw_layer_geotagging ( gpointer lav[2] )
2604 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2605 // Unset so can be reverified later if necessary
2606 vtl->has_verified_thumbnails = FALSE;
2608 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2615 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2618 * Acquire into this TRW Layer straight from GPS Device
2620 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2622 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2623 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2624 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2625 VikViewport *vvp = vik_window_viewport(vw);
2627 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2628 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2632 * Acquire into this TRW Layer from Google Directions
2634 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2636 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2637 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2638 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2639 VikViewport *vvp = vik_window_viewport(vw);
2641 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2644 #ifdef VIK_CONFIG_OPENSTREETMAP
2646 * Acquire into this TRW Layer from OSM
2648 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2650 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2651 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2652 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2653 VikViewport *vvp = vik_window_viewport(vw);
2655 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2659 #ifdef VIK_CONFIG_GEOCACHES
2661 * Acquire into this TRW Layer from Geocaching.com
2663 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2665 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2666 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2667 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2668 VikViewport *vvp = vik_window_viewport(vw);
2670 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2674 #ifdef VIK_CONFIG_GEOTAG
2676 * Acquire into this TRW Layer from images
2678 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2680 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2681 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2682 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2683 VikViewport *vvp = vik_window_viewport(vw);
2685 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2686 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2688 // Reverify thumbnails as they may have changed
2689 vtl->has_verified_thumbnails = FALSE;
2690 trw_layer_verify_thumbnails ( vtl, NULL );
2694 static void trw_layer_new_wp ( gpointer lav[2] )
2696 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2697 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2698 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2699 instead return true if you want to update. */
2700 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 )
2701 vik_layers_panel_emit_update ( vlp );
2704 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2706 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2707 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2709 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2710 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2711 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2712 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2713 vik_layers_panel_emit_update ( vlp );
2717 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2719 /* NB do not care if wp is visible or not */
2720 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2723 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2725 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2726 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2728 /* Only 1 waypoint - jump straight to it */
2729 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2730 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2731 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2733 /* If at least 2 waypoints - find center and then zoom to fit */
2734 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2736 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2737 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2738 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2741 vik_layers_panel_emit_update ( vlp );
2744 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2746 static gpointer pass_along[2];
2748 GtkWidget *export_submenu;
2749 pass_along[0] = vtl;
2750 pass_along[1] = vlp;
2752 item = gtk_menu_item_new();
2753 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2754 gtk_widget_show ( item );
2756 /* Now with icons */
2757 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2758 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2759 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2760 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2761 gtk_widget_show ( item );
2763 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2764 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2765 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2766 gtk_widget_show ( item );
2768 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2770 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2771 gtk_widget_show ( item );
2773 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2774 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2776 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2777 gtk_widget_show ( item );
2779 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2781 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2782 gtk_widget_show ( item );
2784 export_submenu = gtk_menu_new ();
2785 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2786 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2787 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2788 gtk_widget_show ( item );
2789 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2791 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2792 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2793 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2794 gtk_widget_show ( item );
2796 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2797 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2798 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2799 gtk_widget_show ( item );
2801 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2803 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2804 gtk_widget_show ( item );
2806 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2807 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2808 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2809 gtk_widget_show ( item );
2811 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
2812 item = gtk_menu_item_new_with_mnemonic ( external1 );
2813 g_free ( external1 );
2814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
2815 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2816 gtk_widget_show ( item );
2818 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
2819 item = gtk_menu_item_new_with_mnemonic ( external2 );
2820 g_free ( external2 );
2821 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
2822 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2823 gtk_widget_show ( item );
2825 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2826 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2828 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2829 gtk_widget_show ( item );
2831 #ifdef VIK_CONFIG_GEONAMES
2832 GtkWidget *wikipedia_submenu = gtk_menu_new();
2833 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2834 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2835 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2836 gtk_widget_show(item);
2837 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2839 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2840 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2842 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2843 gtk_widget_show ( item );
2845 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2846 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2848 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2849 gtk_widget_show ( item );
2852 #ifdef VIK_CONFIG_GEOTAG
2853 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
2854 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
2855 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2856 gtk_widget_show ( item );
2859 GtkWidget *acquire_submenu = gtk_menu_new ();
2860 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2861 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2862 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2863 gtk_widget_show ( item );
2864 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2866 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2867 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2868 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2869 gtk_widget_show ( item );
2871 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2873 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2874 gtk_widget_show ( item );
2876 #ifdef VIK_CONFIG_OPENSTREETMAP
2877 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2878 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2879 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2880 gtk_widget_show ( item );
2883 #ifdef VIK_CONFIG_GEOCACHES
2884 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2885 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2886 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2887 gtk_widget_show ( item );
2890 #ifdef VIK_CONFIG_GEOTAG
2891 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
2892 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
2893 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2894 gtk_widget_show ( item );
2897 #ifdef VIK_CONFIG_OPENSTREETMAP
2898 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2899 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2900 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2901 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2902 gtk_widget_show ( item );
2905 GtkWidget *delete_submenu = gtk_menu_new ();
2906 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2907 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2908 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2909 gtk_widget_show ( item );
2910 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2912 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2913 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2914 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2915 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2916 gtk_widget_show ( item );
2918 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2919 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2920 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2921 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2922 gtk_widget_show ( item );
2924 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2925 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2926 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2927 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2928 gtk_widget_show ( item );
2930 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2931 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2932 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2933 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2934 gtk_widget_show ( item );
2936 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2937 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2939 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2940 gtk_widget_show ( item );
2943 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2944 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2946 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2947 gtk_widget_show ( item );
2951 // Fake Waypoint UUIDs vi simple increasing integer
2952 static guint wp_uuid = 0;
2954 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2958 vik_waypoint_set_name (wp, name);
2960 if ( VIK_LAYER(vtl)->realized )
2962 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2964 // Visibility column always needed for waypoints
2965 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2966 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 );
2968 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 );
2970 // Actual setting of visibility dependent on the waypoint
2971 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2973 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
2976 highest_wp_number_add_wp(vtl, name);
2977 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
2981 // Fake Track UUIDs vi simple increasing integer
2982 static guint tr_uuid = 0;
2984 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2988 vik_track_set_name (t, name);
2990 if ( VIK_LAYER(vtl)->realized )
2992 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2993 // Visibility column always needed for tracks
2994 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2995 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 );
2997 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 );
2999 // Actual setting of visibility dependent on the track
3000 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3002 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3005 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3009 /* to be called whenever a track has been deleted or may have been changed. */
3010 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3012 if (vtl->current_tp_track == trk )
3013 trw_layer_cancel_current_tp ( vtl, FALSE );
3014 else if (vtl->last_tp_track == trk )
3015 trw_layer_cancel_last_tp ( vtl );
3018 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3021 gchar *newname = g_strdup(name);
3022 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
3023 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
3024 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3026 newname = new_newname;
3032 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3034 // No more uniqueness of name forced when loading from a file
3035 // This now makes this function a little redunant as we just flow the parameters through
3036 vik_trw_layer_add_waypoint ( vtl, name, wp );
3039 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3041 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3042 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3043 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3044 vik_track_free ( tr );
3045 vtl->route_finder_append = FALSE; /* this means we have added it */
3048 // No more uniqueness of name forced when loading from a file
3049 vik_trw_layer_add_track ( vtl, name, tr );
3051 if ( vtl->route_finder_check_added_track ) {
3052 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3053 vtl->route_finder_added_track = tr;
3058 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3060 *l = g_list_append(*l, id);
3064 * Move an item from one TRW layer to another TRW layer
3066 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3068 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3069 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3071 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, trk->name);
3073 VikTrack *trk2 = vik_track_copy ( trk );
3074 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3075 vik_trw_layer_delete_track ( vtl_src, trk );
3078 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3079 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3081 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, wp->name);
3083 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3084 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3085 trw_layer_delete_waypoint ( vtl_src, wp );
3089 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3091 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3092 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3094 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3095 GList *items = NULL;
3098 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3099 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3101 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3102 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3107 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3108 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3110 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3117 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3118 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3123 VikTrack *trk; // input
3124 gpointer uuid; // output
3127 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3129 trku_udata *user_data = udata;
3130 if ( trk == user_data->trk ) {
3131 user_data->uuid = id;
3137 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3139 gboolean was_visible = FALSE;
3141 if ( trk && trk->name ) {
3143 if ( trk == vtl->current_track ) {
3144 vtl->current_track = NULL;
3145 vtl->current_tp_track = NULL;
3146 vtl->current_tp_id = NULL;
3147 vtl->moving_tp = FALSE;
3150 was_visible = trk->visible;
3152 if ( trk == vtl->route_finder_current_track )
3153 vtl->route_finder_current_track = NULL;
3155 if ( trk == vtl->route_finder_added_track )
3156 vtl->route_finder_added_track = NULL;
3162 // Hmmm, want key of it
3163 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3165 if ( trkf && udata.uuid ) {
3166 /* could be current_tp, so we have to check */
3167 trw_layer_cancel_tps_of_track ( vtl, trk );
3169 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3172 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3173 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3174 g_hash_table_remove ( vtl->tracks, udata.uuid );
3181 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3183 gboolean was_visible = FALSE;
3185 if ( wp && wp->name ) {
3187 if ( wp == vtl->current_wp ) {
3188 vtl->current_wp = NULL;
3189 vtl->current_wp_id = NULL;
3190 vtl->moving_wp = FALSE;
3193 was_visible = wp->visible;
3199 // Hmmm, want key of it
3200 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3202 if ( wpf && udata.uuid ) {
3203 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3206 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3207 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3209 highest_wp_number_remove_wp(vtl, wp->name);
3210 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3219 // Only for temporary use by trw_layer_delete_waypoint_by_name
3220 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3222 wpu_udata *user_data = udata;
3223 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3224 user_data->uuid = id;
3231 * Delete a waypoint by the given name
3232 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3233 * as there be multiple waypoints with the same name
3235 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3238 // Fake a waypoint with the given name
3239 udata.wp = vik_waypoint_new ();
3240 vik_waypoint_set_name (udata.wp, name);
3241 // Currently only the name is used in this waypoint find function
3244 // Hmmm, want key of it
3245 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3247 vik_waypoint_free (udata.wp);
3249 if ( wpf && udata.uuid )
3250 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3256 VikTrack *trk; // input
3257 gpointer uuid; // output
3260 // Only for temporary use by trw_layer_delete_track_by_name
3261 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3263 tpu_udata *user_data = udata;
3264 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3265 user_data->uuid = id;
3272 * Delete a track by the given name
3273 * NOTE: ATM this will delete the first encountered Track with the specified name
3274 * as there be multiple track with the same name
3276 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3279 // Fake a track with the given name
3280 udata.trk = vik_track_new ();
3281 vik_track_set_name (udata.trk, name);
3282 // Currently only the name is used in this waypoint find function
3285 // Hmmm, want key of it
3286 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3288 vik_track_free (udata.trk);
3290 if ( trkf && udata.uuid )
3291 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3296 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3298 vik_treeview_item_delete (vt, it );
3301 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3304 vtl->current_track = NULL;
3305 vtl->route_finder_current_track = NULL;
3306 vtl->route_finder_added_track = NULL;
3307 if (vtl->current_tp_track)
3308 trw_layer_cancel_current_tp(vtl, FALSE);
3309 if (vtl->last_tp_track)
3310 trw_layer_cancel_last_tp ( vtl );
3312 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3313 g_hash_table_remove_all(vtl->tracks_iters);
3314 g_hash_table_remove_all(vtl->tracks);
3316 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3319 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3321 vtl->current_wp = NULL;
3322 vtl->current_wp_id = NULL;
3323 vtl->moving_wp = FALSE;
3325 highest_wp_number_reset(vtl);
3327 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3328 g_hash_table_remove_all(vtl->waypoints_iters);
3329 g_hash_table_remove_all(vtl->waypoints);
3331 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3334 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3336 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3337 // Get confirmation from the user
3338 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3339 _("Are you sure you want to delete all tracks in %s?"),
3340 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3341 vik_trw_layer_delete_all_tracks (vtl);
3344 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3346 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3347 // Get confirmation from the user
3348 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3349 _("Are you sure you want to delete all waypoints in %s?"),
3350 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3351 vik_trw_layer_delete_all_waypoints (vtl);
3354 static void trw_layer_delete_item ( gpointer pass_along[6] )
3356 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3357 gboolean was_visible = FALSE;
3358 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3360 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3361 if ( wp && wp->name ) {
3362 if ( GPOINTER_TO_INT ( pass_along[4]) )
3363 // Get confirmation from the user
3364 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3365 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3366 _("Are you sure you want to delete the waypoint \"%s\""),
3369 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3374 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3375 if ( trk && trk->name ) {
3376 if ( GPOINTER_TO_INT ( pass_along[4]) )
3377 // Get confirmation from the user
3378 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3379 _("Are you sure you want to delete the track \"%s\""),
3382 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3386 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3390 static void trw_layer_properties_item ( gpointer pass_along[7] )
3392 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3393 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3395 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3397 if ( wp && wp->name )
3399 gboolean updated = FALSE;
3400 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3402 if ( updated && VIK_LAYER(vtl)->visible )
3403 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3408 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3409 if ( tr && tr->name )
3411 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3413 pass_along[1], /* vlp */
3414 pass_along[5] ); /* vvp */
3420 Parameter 1 -> VikLayersPanel
3421 Parameter 2 -> VikLayer
3422 Parameter 3 -> VikViewport
3424 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3427 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3428 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3431 /* since vlp not set, vl & vvp should be valid instead! */
3433 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3434 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3439 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3441 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3442 if ( trps && trps->data )
3443 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3446 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3448 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
3449 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3450 if ( trps && *trps )
3452 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3454 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3455 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3456 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3457 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3458 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3462 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3464 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3465 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3467 vtl->current_track = track;
3468 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3470 if ( track->trackpoints )
3471 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3475 * extend a track using route finder
3477 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3479 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3480 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3481 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3483 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3484 vtl->route_finder_coord = last_coord;
3485 vtl->route_finder_current_track = track;
3486 vtl->route_finder_started = TRUE;
3488 if ( track->trackpoints )
3489 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3493 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3495 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3496 /* Also warn if overwrite old elevation data */
3497 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3499 vik_track_apply_dem_data ( track );
3502 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3504 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3507 trps = g_list_last(trps);
3508 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3511 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3513 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3516 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3519 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3521 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3524 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3527 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3529 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3532 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3536 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3538 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3540 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3541 if ( trps && *trps )
3543 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3544 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3545 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3546 if ( pass_along[1] )
3547 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3549 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3553 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3555 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3556 trw_layer_tpwin_init ( vtl );
3559 /*************************************
3560 * merge/split by time routines
3561 *************************************/
3563 /* called for each key in track hash table.
3564 * If the current track has the same time stamp type, add it to the result,
3565 * except the one pointed by "exclude".
3566 * set exclude to NULL if there is no exclude to check.
3567 * Note that the result is in reverse (for performance reasons).
3572 gboolean with_timestamps;
3574 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3576 twt_udata *user_data = udata;
3577 VikTrackpoint *p1, *p2;
3579 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3583 if (VIK_TRACK(value)->trackpoints) {
3584 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3585 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3587 if ( user_data->with_timestamps ) {
3588 if (!p1->has_timestamp || !p2->has_timestamp) {
3593 // Don't add tracks with timestamps when getting non timestamp tracks
3594 if (p1->has_timestamp || p2->has_timestamp) {
3600 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3603 /* called for each key in track hash table. if original track user_data[1] is close enough
3604 * to the passed one, add it to list in user_data[0]
3606 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3609 VikTrackpoint *p1, *p2;
3611 GList **nearby_tracks = ((gpointer *)user_data)[0];
3612 GList *orig_track = ((gpointer *)user_data)[1];
3613 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3616 * detect reasons for not merging, and return
3617 * if no reason is found not to merge, then do it.
3620 if (VIK_TRACK(value)->trackpoints == orig_track) {
3624 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3625 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3627 if (VIK_TRACK(value)->trackpoints) {
3628 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3629 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3631 if (!p1->has_timestamp || !p2->has_timestamp) {
3632 g_print("no timestamp\n");
3636 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3637 if (! (abs(t1 - p2->timestamp) < thr*60 ||
3639 abs(p1->timestamp - t2) < thr*60)
3646 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3649 /* comparison function used to sort tracks; a and b are hash table keys */
3650 /* Not actively used - can be restored if needed
3651 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3653 GHashTable *tracks = user_data;
3656 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3657 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3659 if (t1 < t2) return -1;
3660 if (t1 > t2) return 1;
3665 /* comparison function used to sort trackpoints */
3666 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3668 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3670 if (t1 < t2) return -1;
3671 if (t1 > t2) return 1;
3676 * comparison function which can be used to sort tracks or waypoints by name
3678 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3680 const gchar* namea = (const gchar*) a;
3681 const gchar* nameb = (const gchar*) b;
3682 if ( namea == NULL || nameb == NULL)
3685 // Same sort method as used in the vik_treeview_*_alphabetize functions
3686 return strcmp ( namea, nameb );
3690 * Attempt to merge selected track with other tracks specified by the user
3691 * Tracks to merge with must be of the same 'type' as the selected track -
3692 * either all with timestamps, or all without timestamps
3694 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3696 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3697 GList *other_tracks = NULL;
3698 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3700 if ( !track->trackpoints )
3704 udata.result = &other_tracks;
3705 udata.exclude = track->trackpoints;
3706 // Allow merging with 'similar' time type time tracks
3707 // i.e. either those times, or those without
3708 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3710 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3711 other_tracks = g_list_reverse(other_tracks);
3713 if ( !other_tracks ) {
3714 if ( udata.with_timestamps )
3715 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3717 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3721 // Sort alphabetically for user presentation
3722 // Convert into list of names for usage with dialog function
3723 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3724 GList *other_tracks_names = NULL;
3725 GList *iter = g_list_first ( other_tracks );
3727 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
3728 iter = g_list_next ( iter );
3731 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
3733 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3734 other_tracks_names, TRUE,
3735 _("Merge with..."), _("Select track to merge with"));
3736 g_list_free(other_tracks);
3737 g_list_free(other_tracks_names);
3742 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3743 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
3745 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3746 merge_track->trackpoints = NULL;
3747 vik_trw_layer_delete_track (vtl, merge_track);
3748 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3751 /* TODO: free data before free merge_list */
3752 for (l = merge_list; l != NULL; l = g_list_next(l))
3754 g_list_free(merge_list);
3755 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3759 /* merge by time routine */
3760 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3762 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3765 GList *nearby_tracks;
3767 static guint thr = 1;
3768 guint track_count = 0;
3770 GList *tracks_with_timestamp = NULL;
3771 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3772 if (orig_trk->trackpoints &&
3773 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
3774 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3779 udata.result = &tracks_with_timestamp;
3780 udata.exclude = orig_trk->trackpoints;
3781 udata.with_timestamps = TRUE;
3782 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3783 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3785 if (!tracks_with_timestamp) {
3786 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3789 g_list_free(tracks_with_timestamp);
3791 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3792 _("Merge Threshold..."),
3793 _("Merge when time between tracks less than:"),
3798 /* merge tracks until we can't */
3800 nearby_tracks = NULL;
3804 // Need to refind original track incase we've deleted and recreated it??
3805 //track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3806 trps = orig_trk->trackpoints;
3811 if (nearby_tracks) {
3812 g_list_free(nearby_tracks);
3813 nearby_tracks = NULL;
3816 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3817 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3819 /* g_print("Original track times: %d and %d\n", t1, t2); */
3820 params[0] = &nearby_tracks;
3822 params[2] = GUINT_TO_POINTER (thr);
3824 /* get a list of adjacent-in-time tracks */
3825 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3827 /* add original track */
3828 nearby_tracks = g_list_prepend(nearby_tracks, orig_trk);
3832 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, ((x)->data)))
3833 GList *l = nearby_tracks;
3834 // VikTrack *tr = vik_track_new();
3835 //tr->visible = track->visible;
3839 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3840 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3842 t1 = get_first_trackpoint(l)->timestamp;
3843 t2 = get_last_trackpoint(l)->timestamp;
3844 #undef get_first_trackpoint
3845 #undef get_last_trackpoint
3846 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3850 /* remove trackpoints from merged track, delete track */
3851 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, get_track(l)->trackpoints);
3852 get_track(l)->trackpoints = NULL;
3853 vik_trw_layer_delete_track (vtl, l->data);
3859 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
3860 //vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3863 } while (track_count > 1);
3864 g_list_free(nearby_tracks);
3865 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3868 /* split by time routine */
3869 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3871 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3872 GList *trps = track->trackpoints;
3874 GList *newlists = NULL;
3875 GList *newtps = NULL;
3877 static guint thr = 1;
3884 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3885 _("Split Threshold..."),
3886 _("Split when time between trackpoints exceeds:"),
3891 /* iterate through trackpoints, and copy them into new lists without touching original list */
3892 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3896 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3898 g_print("panic: ts < prev_ts: this should never happen!\n");
3901 if (ts - prev_ts > thr*60) {
3902 /* flush accumulated trackpoints into new list */
3903 newlists = g_list_append(newlists, g_list_reverse(newtps));
3907 /* accumulate trackpoint copies in newtps, in reverse order */
3908 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3910 iter = g_list_next(iter);
3913 newlists = g_list_append(newlists, g_list_reverse(newtps));
3916 /* put lists of trackpoints into tracks */
3919 // Only bother updating if the split results in new tracks
3920 if (g_list_length (newlists) > 1) {
3925 tr = vik_track_new();
3926 tr->visible = track->visible;
3927 tr->trackpoints = (GList *)(iter->data);
3929 new_tr_name = g_strdup_printf ("%s #%d", track->name, i++);
3930 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3931 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3932 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3934 iter = g_list_next(iter);
3936 // Remove original track and then update the display
3937 vik_trw_layer_delete_track (VIK_TRW_LAYER(pass_along[0]), track);
3938 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3940 g_list_free(newlists);
3944 * Split a track by the number of points as specified by the user
3946 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3948 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3949 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3951 // Check valid track
3952 GList *trps = track->trackpoints;
3956 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3957 _("Split Every Nth Point"),
3958 _("Split on every Nth point:"),
3959 250, // Default value as per typical limited track capacity of various GPS devices
3963 // Was a valid number returned?
3969 GList *newlists = NULL;
3970 GList *newtps = NULL;
3975 /* accumulate trackpoint copies in newtps, in reverse order */
3976 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3978 if (count >= points) {
3979 /* flush accumulated trackpoints into new list */
3980 newlists = g_list_append(newlists, g_list_reverse(newtps));
3984 iter = g_list_next(iter);
3987 // If there is a remaining chunk put that into the new split list
3988 // This may well be the whole track if no split points were encountered
3990 newlists = g_list_append(newlists, g_list_reverse(newtps));
3993 /* put lists of trackpoints into tracks */
3996 // Only bother updating if the split results in new tracks
3997 if (g_list_length (newlists) > 1) {
4002 tr = vik_track_new();
4003 tr->visible = track->visible;
4004 tr->trackpoints = (GList *)(iter->data);
4006 new_tr_name = g_strdup_printf ("%s #%d", track->name, i++);
4007 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
4009 iter = g_list_next(iter);
4011 // Remove original track and then update the display
4012 vik_trw_layer_delete_track (VIK_TRW_LAYER(pass_along[0]), track);
4013 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4015 g_list_free(newlists);
4018 /* end of split/merge routines */
4023 static void trw_layer_reverse ( gpointer pass_along[6] )
4025 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4026 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4028 // Check valid track
4029 GList *trps = track->trackpoints;
4033 vik_track_reverse ( track );
4035 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4039 * Similar to trw_layer_enum_item, but this uses a sorted method
4042 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4044 GList **list = (GList**)udata;
4045 // *list = g_list_prepend(*all, key); //unsorted method
4046 // Sort named list alphabetically
4047 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4052 * Now Waypoint specific sort
4054 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4056 GList **list = (GList**)udata;
4057 // Sort named list alphabetically
4058 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4062 * Track specific sort
4064 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4066 GList **list = (GList**)udata;
4067 // Sort named list alphabetically
4068 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4073 gboolean has_same_track_name;
4074 const gchar *same_track_name;
4075 } same_track_name_udata;
4077 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4079 const gchar* namea = (const gchar*) aa;
4080 const gchar* nameb = (const gchar*) bb;
4083 gint result = strcmp ( namea, nameb );
4085 if ( result == 0 ) {
4086 // Found two names the same
4087 same_track_name_udata *user_data = udata;
4088 user_data->has_same_track_name = TRUE;
4089 user_data->same_track_name = namea;
4092 // Leave ordering the same
4097 * Find out if any tracks have the same name in this layer
4099 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4101 // Sort items by name, then compare if any next to each other are the same
4103 GList *track_names = NULL;
4104 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4107 if ( ! track_names )
4110 same_track_name_udata udata;
4111 udata.has_same_track_name = FALSE;
4113 // Use sort routine to traverse list comparing items
4114 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4115 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4116 // Still no tracks...
4120 return udata.has_same_track_name;
4124 * Force unqiue track names for this layer
4125 * Note the panel is a required parameter to enable the update of the names displayed
4127 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4129 // . Search list for an instance of repeated name
4130 // . get track of this name
4131 // . create new name
4132 // . rename track & update equiv. treeview iter
4133 // . repeat until all different
4135 same_track_name_udata udata;
4137 GList *track_names = NULL;
4138 udata.has_same_track_name = FALSE;
4139 udata.same_track_name = NULL;
4141 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4144 if ( ! track_names )
4147 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4149 // Still no tracks...
4150 if ( ! dummy_list1 )
4153 while ( udata.has_same_track_name ) {
4155 // Find a track with the same name
4156 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4160 g_critical("Houston, we've had a problem.");
4161 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4162 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4167 gchar *newname = get_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4168 vik_track_set_name ( trk, newname );
4174 // Need want key of it for treeview update
4175 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4177 if ( trkf && udataU.uuid ) {
4179 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4182 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4183 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4184 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4189 // Start trying to find same names again...
4191 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4192 udata.has_same_track_name = FALSE;
4193 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4195 // No tracks any more - give up searching
4196 if ( ! dummy_list2 )
4197 udata.has_same_track_name = FALSE;
4201 vik_layers_panel_emit_update ( vlp );
4207 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4209 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4212 // Ensure list of track names offered is unique
4213 if ( trw_layer_has_same_track_names ( vtl ) ) {
4214 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4215 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4216 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4222 // Sort list alphabetically for better presentation
4223 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4226 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4230 // Get list of items to delete from the user
4231 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4234 _("Delete Selection"),
4235 _("Select tracks to delete"));
4238 // Delete requested tracks
4239 // since specificly requested, IMHO no need for extra confirmation
4240 if ( delete_list ) {
4242 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4243 // This deletes first trk it finds of that name (but uniqueness is enforced above)
4244 trw_layer_delete_track_by_name (vtl, l->data);
4246 g_list_free(delete_list);
4247 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4254 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4256 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4259 // TODO consider disabling this with warning message about
4260 // not working due to multiple same names
4261 // enable calling (yet to be defined) uniqify method?
4263 // Sort list alphabetically for better presentation
4264 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4266 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4270 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4272 // Get list of items to delete from the user
4273 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4276 _("Delete Selection"),
4277 _("Select waypoints to delete"));
4280 // Delete requested waypoints
4281 // since specificly requested, IMHO no need for extra confirmation
4282 if ( delete_list ) {
4284 for (l = delete_list; l != NULL; l = g_list_next(l)) {
4285 // TODO Hmmm conversion needed here -- not 1:1 relationship any more of name to reference
4286 // -- new functionality / or need to extend list to have uuid with it / or have uuids and ways to find out the name for display
4287 // may need to rework the general dialog functions
4288 // ARM This deletes first WP it finds of that name
4289 trw_layer_delete_waypoint_by_name (vtl, l->data);
4291 g_list_free(delete_list);
4292 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4297 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4299 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4301 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4304 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4306 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4307 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4311 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4313 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4315 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
4317 // No actual change to the name supplied
4318 if (strcmp(newname, wp->name) == 0 )
4321 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4324 // An existing waypoint has been found with the requested name
4325 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4326 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
4331 // Update WP name and refresh the treeview
4332 vik_waypoint_set_name (wp, newname);
4334 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4335 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4338 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4343 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4345 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
4347 // No actual change to the name supplied
4348 if (strcmp(newname, trk->name) == 0)
4351 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4354 // An existing track has been found with the requested name
4355 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4356 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4360 // Update track name and refresh GUI parts
4361 vik_track_set_name (trk, newname);
4363 // Update any subwindows that could be displaying this track which has changed name
4364 // Only one Track Edit Window
4365 if ( l->current_tp_track == trk && l->tpwin ) {
4366 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
4368 // Property Dialog of the track
4369 vik_trw_layer_propwin_update ( trk );
4371 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4372 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4375 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4382 static gboolean is_valid_geocache_name ( gchar *str )
4384 gint len = strlen ( str );
4385 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]));
4388 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
4390 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4391 a_acquire_set_filter_track ( trk );
4394 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
4396 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
4397 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
4400 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
4402 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4404 gchar *escaped = uri_escape ( tr->comment );
4405 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4406 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4412 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4413 /* viewpoint is now available instead */
4414 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4416 static gpointer pass_along[8];
4418 gboolean rv = FALSE;
4421 pass_along[1] = vlp;
4422 pass_along[2] = GINT_TO_POINTER (subtype);
4423 pass_along[3] = sublayer;
4424 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
4425 pass_along[5] = vvp;
4426 pass_along[6] = iter;
4427 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
4429 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4433 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
4434 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
4435 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4436 gtk_widget_show ( item );
4438 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4439 VikTrwLayer *vtl = l;
4440 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
4441 if (tr && tr->property_dialog)
4442 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
4445 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
4446 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
4447 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4448 gtk_widget_show ( item );
4450 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
4451 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
4452 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4453 gtk_widget_show ( item );
4455 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
4456 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
4457 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4458 gtk_widget_show ( item );
4460 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4462 gboolean separator_created = FALSE;
4464 /* could be a right-click using the tool */
4465 if ( vlp != NULL ) {
4466 item = gtk_menu_item_new ();
4467 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4468 gtk_widget_show ( item );
4470 separator_created = TRUE;
4472 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4473 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4474 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4475 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4476 gtk_widget_show ( item );
4479 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4481 if ( wp && wp->name ) {
4482 if ( is_valid_geocache_name ( wp->name ) ) {
4484 if ( !separator_created ) {
4485 item = gtk_menu_item_new ();
4486 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4487 gtk_widget_show ( item );
4488 separator_created = TRUE;
4491 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
4492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
4493 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4494 gtk_widget_show ( item );
4498 if ( wp && wp->image )
4500 if ( !separator_created ) {
4501 item = gtk_menu_item_new ();
4502 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4503 gtk_widget_show ( item );
4504 separator_created = TRUE;
4507 // Set up image paramater
4508 pass_along[5] = wp->image;
4510 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
4511 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
4512 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
4513 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4514 gtk_widget_show ( item );
4516 #ifdef VIK_CONFIG_GEOTAG
4517 GtkWidget *geotag_submenu = gtk_menu_new ();
4518 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
4519 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
4520 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4521 gtk_widget_show ( item );
4522 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
4524 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
4525 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
4526 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4527 gtk_widget_show ( item );
4529 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
4530 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
4531 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4532 gtk_widget_show ( item );
4539 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
4542 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
4543 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4544 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4545 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4546 gtk_widget_show ( item );
4549 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
4551 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
4552 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4553 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4554 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4555 gtk_widget_show ( item );
4557 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4558 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4560 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4561 gtk_widget_show ( item );
4563 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
4564 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4566 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4567 gtk_widget_show ( item );
4569 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
4570 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4572 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4573 gtk_widget_show ( item );
4576 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
4580 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
4581 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4582 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4583 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4584 gtk_widget_show ( item );
4586 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
4587 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4588 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4589 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4590 gtk_widget_show ( item );
4592 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
4593 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4594 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4595 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4596 gtk_widget_show ( item );
4599 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4601 GtkWidget *goto_submenu;
4602 item = gtk_menu_item_new ();
4603 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4604 gtk_widget_show ( item );
4606 goto_submenu = gtk_menu_new ();
4607 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4608 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4609 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4610 gtk_widget_show ( item );
4611 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
4613 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
4614 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
4615 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
4616 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4617 gtk_widget_show ( item );
4619 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
4620 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4621 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
4622 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4623 gtk_widget_show ( item );
4625 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
4626 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
4627 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
4628 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4629 gtk_widget_show ( item );
4631 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
4632 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
4633 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
4634 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4635 gtk_widget_show ( item );
4637 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4638 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4639 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4640 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4641 gtk_widget_show ( item );
4643 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4644 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4645 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4646 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4647 gtk_widget_show ( item );
4649 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4650 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4651 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4652 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4653 gtk_widget_show ( item );
4655 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4657 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4658 gtk_widget_show ( item );
4660 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4661 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4662 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4663 gtk_widget_show ( item );
4665 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4666 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4667 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4668 gtk_widget_show ( item );
4670 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4672 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4673 gtk_widget_show ( item );
4675 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
4676 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
4677 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
4678 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4679 gtk_widget_show ( item );
4681 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4683 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4684 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
4685 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4686 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4687 gtk_widget_show ( item );
4690 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4691 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
4692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), 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 ( _("Export Trac_k as GPX...") );
4697 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4698 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), 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 ( _("E_xtend Track End") );
4703 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4705 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4706 gtk_widget_show ( item );
4708 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4709 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
4710 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4711 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4712 gtk_widget_show ( item );
4714 #ifdef VIK_CONFIG_OPENSTREETMAP
4715 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4716 // Convert internal pointer into actual track for usage outside this file
4717 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
4718 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4719 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4720 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4721 gtk_widget_show ( item );
4724 if ( is_valid_google_route ( l, sublayer ) )
4726 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4727 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4728 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4729 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4730 gtk_widget_show ( item );
4733 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4734 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4735 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4736 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4737 gtk_widget_show ( item );
4739 /* ATM This function is only available via the layers panel, due to needing a vlp */
4741 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4742 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4743 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4745 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4746 gtk_widget_show ( item );
4750 #ifdef VIK_CONFIG_GEOTAG
4751 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4752 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
4753 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4754 gtk_widget_show ( item );
4757 // Only show on viewport popmenu when a trackpoint is selected
4758 if ( ! vlp && l->current_tpl ) {
4760 item = gtk_menu_item_new ();
4761 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4762 gtk_widget_show ( item );
4764 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4765 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4766 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4767 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4768 gtk_widget_show ( item );
4776 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4779 if (!vtl->current_tpl)
4781 if (!vtl->current_tpl->next)
4784 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4785 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4787 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4790 VikTrackpoint *tp_new = vik_trackpoint_new();
4791 struct LatLon ll_current, ll_next;
4792 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4793 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4795 /* main positional interpolation */
4796 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4797 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4799 /* Now other properties that can be interpolated */
4800 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4802 if (tp_current->has_timestamp && tp_next->has_timestamp) {
4803 /* Note here the division is applied to each part, then added
4804 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4805 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4806 tp_new->has_timestamp = TRUE;
4809 if (tp_current->speed != NAN && tp_next->speed != NAN)
4810 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4812 /* TODO - improve interpolation of course, as it may not be correct.
4813 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4814 [similar applies if value is in radians] */
4815 if (tp_current->course != NAN && tp_next->course != NAN)
4816 tp_new->speed = (tp_current->course + tp_next->course)/2;
4818 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4820 /* Insert new point into the trackpoints list after the current TP */
4821 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
4822 gint index = g_list_index ( tr->trackpoints, tp_current );
4824 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4829 /* to be called when last_tpl no longer exists. */
4830 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4832 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4833 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4834 vtl->last_tpl = NULL;
4835 vtl->last_tp_track = NULL;
4838 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4844 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4848 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4850 if ( vtl->current_tpl )
4852 vtl->current_tpl = NULL;
4853 vtl->current_tp_track = NULL;
4854 vtl->current_tp_id = NULL;
4855 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4859 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4861 g_assert ( vtl->tpwin != NULL );
4862 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4863 trw_layer_cancel_current_tp ( vtl, TRUE );
4865 if ( vtl->current_tpl == NULL )
4868 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4870 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4871 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4873 VikTrack *tr = vik_track_new ();
4874 GList *newglist = g_list_alloc ();
4875 newglist->prev = NULL;
4876 newglist->next = vtl->current_tpl->next;
4877 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4878 tr->trackpoints = newglist;
4880 vtl->current_tpl->next->prev = newglist; /* end old track here */
4881 vtl->current_tpl->next = NULL;
4883 vtl->current_tpl = newglist; /* change tp to first of new track. */
4884 vtl->current_tp_track->name = name;
4886 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
4890 vik_trw_layer_add_track ( vtl, name, tr );
4891 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4894 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4896 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
4901 /* can't join with a non-existent trackpoint */
4902 vtl->last_tpl = NULL;
4903 vtl->last_tp_track = NULL;
4905 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4907 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4908 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4910 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4912 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4913 if ( vtl->current_tp_track )
4914 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
4916 trw_layer_cancel_last_tp ( vtl );
4918 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4919 g_list_free_1 ( vtl->current_tpl );
4920 vtl->current_tpl = new_tpl;
4921 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4925 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4926 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4927 g_list_free_1 ( vtl->current_tpl );
4928 trw_layer_cancel_current_tp ( vtl, FALSE );
4931 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4933 vtl->last_tpl = vtl->current_tpl;
4934 if ( vtl->current_tp_track )
4935 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
4936 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4938 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4940 vtl->last_tpl = vtl->current_tpl;
4941 if ( vtl->current_tp_track )
4942 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
4943 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4945 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4947 // Check tracks exist and are different before joining
4948 if ( ! vtl->last_tp_track || ! vtl->current_tp_track || vtl->last_tp_track == vtl->current_tp_track )
4951 VikTrack *tr1 = vtl->last_tp_track;
4952 VikTrack *tr2 = vtl->current_tp_track;
4954 VikTrack *tr_first = tr1, *tr_last = tr2;
4956 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4957 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4958 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4959 vik_track_reverse ( tr1 );
4960 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4965 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4967 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. */
4968 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4969 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4970 tr2->trackpoints = NULL;
4972 vtl->current_tp_track = vtl->last_tp_track; /* current_tp stays the same (believe it or not!) */
4973 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
4975 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4976 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4977 vik_trw_layer_delete_track ( vtl, vtl->current_tp_track );
4979 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4980 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4982 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4984 trw_layer_insert_tp_after_current_tp ( vtl );
4985 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4987 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4988 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4991 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4995 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4996 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4997 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4998 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4999 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5001 if ( vtl->current_tpl )
5002 if ( vtl->current_tp_track )
5003 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5004 /* set layer name and TP data */
5007 /***************************************************************************
5009 ***************************************************************************/
5011 /*** Utility data structures and functions ****/
5015 gint closest_x, closest_y;
5016 gpointer *closest_wp_id;
5017 VikWaypoint *closest_wp;
5023 gint closest_x, closest_y;
5024 gpointer closest_track_id;
5025 VikTrackpoint *closest_tp;
5030 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5036 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5038 // If waypoint has an image then use the image size to select
5040 gint slackx, slacky;
5041 slackx = wp->image_width / 2;
5042 slacky = wp->image_height / 2;
5044 if ( x <= params->x + slackx && x >= params->x - slackx
5045 && y <= params->y + slacky && y >= params->y - slacky ) {
5046 params->closest_wp_id = id;
5047 params->closest_wp = wp;
5048 params->closest_x = x;
5049 params->closest_y = y;
5052 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5053 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5054 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5056 params->closest_wp_id = id;
5057 params->closest_wp = wp;
5058 params->closest_x = x;
5059 params->closest_y = y;
5063 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5065 GList *tpl = t->trackpoints;
5074 tp = VIK_TRACKPOINT(tpl->data);
5076 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5078 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5079 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5080 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5082 params->closest_track_id = id;
5083 params->closest_tp = tp;
5084 params->closest_tpl = tpl;
5085 params->closest_x = x;
5086 params->closest_y = y;
5092 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5094 TPSearchParams params;
5098 params.closest_track_id = NULL;
5099 params.closest_tp = NULL;
5100 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5101 return params.closest_tp;
5104 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5106 WPSearchParams params;
5110 params.closest_wp = NULL;
5111 params.closest_wp_id = NULL;
5112 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5113 return params.closest_wp;
5117 // Some forward declarations
5118 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5119 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5120 static void marker_end_move ( tool_ed_t *t );
5123 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5127 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5129 // Here always allow snapping back to the original location
5130 // this is useful when one decides not to move the thing afterall
5131 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5134 if ( event->state & GDK_CONTROL_MASK )
5136 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5138 new_coord = tp->coord;
5142 if ( event->state & GDK_SHIFT_MASK )
5144 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5146 new_coord = wp->coord;
5150 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5152 marker_moveto ( t, x, y );
5159 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5161 if ( t->holding && event->button == 1 )
5164 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5167 if ( event->state & GDK_CONTROL_MASK )
5169 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5171 new_coord = tp->coord;
5175 if ( event->state & GDK_SHIFT_MASK )
5177 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5179 new_coord = wp->coord;
5182 marker_end_move ( t );
5184 // Determine if working on a waypoint or a trackpoint
5185 if ( t->is_waypoint )
5186 vtl->current_wp->coord = new_coord;
5188 if ( vtl->current_tpl ) {
5189 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5192 if ( vtl->current_tp_track )
5193 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5195 // Don't really know what this is for but seems like it might be handy...
5196 /* can't join with itself! */
5197 trw_layer_cancel_last_tp ( vtl );
5202 vtl->current_wp = NULL;
5203 vtl->current_wp_id = NULL;
5204 trw_layer_cancel_current_tp ( vtl, FALSE );
5206 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5213 Returns true if a waypoint or track is found near the requested event position for this particular layer
5214 The item found is automatically selected
5215 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5217 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5219 if ( event->button != 1 )
5222 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5225 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5228 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5230 if (vtl->waypoints_visible) {
5231 WPSearchParams wp_params;
5232 wp_params.vvp = vvp;
5233 wp_params.x = event->x;
5234 wp_params.y = event->y;
5235 wp_params.closest_wp_id = NULL;
5236 wp_params.closest_wp = NULL;
5238 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5240 if ( wp_params.closest_wp ) {
5243 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5245 // Too easy to move it so must be holding shift to start immediately moving it
5246 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5247 if ( event->state & GDK_SHIFT_MASK ||
5248 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5249 // Put into 'move buffer'
5250 // NB vvp & vw already set in tet
5251 tet->vtl = (gpointer)vtl;
5252 tet->is_waypoint = TRUE;
5254 marker_begin_move (tet, event->x, event->y);
5257 vtl->current_wp = wp_params.closest_wp;
5258 vtl->current_wp_id = wp_params.closest_wp_id;
5260 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5266 if (vtl->tracks_visible) {
5267 TPSearchParams tp_params;
5268 tp_params.vvp = vvp;
5269 tp_params.x = event->x;
5270 tp_params.y = event->y;
5271 tp_params.closest_track_id = NULL;
5272 tp_params.closest_tp = NULL;
5274 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5276 if ( tp_params.closest_tp ) {
5278 // Always select + highlight the track
5279 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5281 tet->is_waypoint = FALSE;
5283 // Select the Trackpoint
5284 // Can move it immediately when control held or it's the previously selected tp
5285 if ( event->state & GDK_CONTROL_MASK ||
5286 vtl->current_tpl == tp_params.closest_tpl ) {
5287 // Put into 'move buffer'
5288 // NB vvp & vw already set in tet
5289 tet->vtl = (gpointer)vtl;
5290 marker_begin_move (tet, event->x, event->y);
5293 vtl->current_tpl = tp_params.closest_tpl;
5294 vtl->current_tp_id = tp_params.closest_track_id;
5295 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5297 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5300 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5302 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5307 /* these aren't the droids you're looking for */
5308 vtl->current_wp = NULL;
5309 vtl->current_wp_id = NULL;
5310 trw_layer_cancel_current_tp ( vtl, FALSE );
5313 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5318 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5320 if ( event->button != 3 )
5323 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5326 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5329 /* Post menu for the currently selected item */
5331 /* See if a track is selected */
5332 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5333 if ( track && track->visible ) {
5335 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
5337 if ( vtl->track_right_click_menu )
5338 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5340 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5342 trw_layer_sublayer_add_menu_items ( vtl,
5343 vtl->track_right_click_menu,
5345 VIK_TRW_LAYER_SUBLAYER_TRACK,
5346 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
5347 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
5350 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5356 /* See if a waypoint is selected */
5357 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5358 if ( waypoint && waypoint->visible ) {
5359 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
5361 if ( vtl->wp_right_click_menu )
5362 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
5364 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5365 trw_layer_sublayer_add_menu_items ( vtl,
5366 vtl->wp_right_click_menu,
5368 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5369 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
5370 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
5372 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5381 /* background drawing hook, to be passed the viewport */
5382 static gboolean tool_sync_done = TRUE;
5384 static gboolean tool_sync(gpointer data)
5386 VikViewport *vvp = data;
5387 gdk_threads_enter();
5388 vik_viewport_sync(vvp);
5389 tool_sync_done = TRUE;
5390 gdk_threads_leave();
5394 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
5397 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
5398 gdk_gc_set_function ( t->gc, GDK_INVERT );
5399 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5400 vik_viewport_sync(t->vvp);
5405 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
5407 VikViewport *vvp = t->vvp;
5408 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5409 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5413 if (tool_sync_done) {
5414 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
5415 tool_sync_done = FALSE;
5419 static void marker_end_move ( tool_ed_t *t )
5421 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5422 g_object_unref ( t->gc );
5426 /*** Edit waypoint ****/
5428 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5430 tool_ed_t *t = g_new(tool_ed_t, 1);
5436 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5438 WPSearchParams params;
5439 tool_ed_t *t = data;
5440 VikViewport *vvp = t->vvp;
5442 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5449 if ( !vtl->vl.visible || !vtl->waypoints_visible )
5452 if ( vtl->current_wp && vtl->current_wp->visible )
5454 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5456 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5458 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5459 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5461 if ( event->button == 3 )
5462 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5464 marker_begin_move(t, event->x, event->y);
5471 params.x = event->x;
5472 params.y = event->y;
5473 params.closest_wp_id = NULL;
5474 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5475 params.closest_wp = NULL;
5476 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
5477 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5479 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5480 marker_begin_move(t, event->x, event->y);
5481 g_critical("shouldn't be here");
5484 else if ( params.closest_wp )
5486 if ( event->button == 3 )
5487 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5489 vtl->waypoint_rightclick = FALSE;
5491 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
5493 vtl->current_wp = params.closest_wp;
5494 vtl->current_wp_id = params.closest_wp_id;
5496 /* could make it so don't update if old WP is off screen and new is null but oh well */
5497 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5501 vtl->current_wp = NULL;
5502 vtl->current_wp_id = NULL;
5503 vtl->waypoint_rightclick = FALSE;
5504 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5508 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5510 tool_ed_t *t = data;
5511 VikViewport *vvp = t->vvp;
5513 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5518 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5521 if ( event->state & GDK_CONTROL_MASK )
5523 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5525 new_coord = tp->coord;
5529 if ( event->state & GDK_SHIFT_MASK )
5531 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5532 if ( wp && wp != vtl->current_wp )
5533 new_coord = wp->coord;
5538 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5540 marker_moveto ( t, x, y );
5547 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5549 tool_ed_t *t = data;
5550 VikViewport *vvp = t->vvp;
5552 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5555 if ( t->holding && event->button == 1 )
5558 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5561 if ( event->state & GDK_CONTROL_MASK )
5563 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5565 new_coord = tp->coord;
5569 if ( event->state & GDK_SHIFT_MASK )
5571 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5572 if ( wp && wp != vtl->current_wp )
5573 new_coord = wp->coord;
5576 marker_end_move ( t );
5578 vtl->current_wp->coord = new_coord;
5579 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5582 /* PUT IN RIGHT PLACE!!! */
5583 if ( event->button == 3 && vtl->waypoint_rightclick )
5585 if ( vtl->wp_right_click_menu )
5586 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5587 if ( vtl->current_wp ) {
5588 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5589 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 );
5590 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5592 vtl->waypoint_rightclick = FALSE;
5597 /**** Begin track ***/
5598 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5603 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5605 vtl->current_track = NULL;
5606 return tool_new_track_click ( vtl, event, vvp );
5609 /*** New track ****/
5611 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5619 gint x1,y1,x2,y2,x3,y3;
5621 } new_track_move_passalong_t;
5623 /* sync and undraw, but only when we have time */
5624 static gboolean ct_sync ( gpointer passalong )
5626 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5628 vik_viewport_sync ( p->vvp );
5629 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5630 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5631 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);
5632 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5634 g_free ( (gpointer) p->str ) ;
5635 p->vtl->ct_sync_done = TRUE;
5640 static const gchar* distance_string (gdouble distance)
5644 /* draw label with distance */
5645 vik_units_distance_t dist_units = a_vik_get_units_distance ();
5646 switch (dist_units) {
5647 case VIK_UNITS_DISTANCE_MILES:
5648 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5649 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5650 } else if (distance < 1609.4) {
5651 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5653 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5657 // VIK_UNITS_DISTANCE_KILOMETRES
5658 if (distance >= 1000 && distance < 100000) {
5659 g_sprintf(str, "%3.2f km", distance/1000.0);
5660 } else if (distance < 1000) {
5661 g_sprintf(str, "%d m", (int)distance);
5663 g_sprintf(str, "%d km", (int)distance/1000);
5667 return g_strdup (str);
5671 * Actually set the message in statusbar
5673 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5675 // Only show elevation data when track has some elevation properties
5676 gchar str_gain_loss[64];
5677 str_gain_loss[0] = '\0';
5679 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5680 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5681 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5683 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5686 // Write with full gain/loss information
5687 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5688 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5693 * Figure out what information should be set in the statusbar and then write it
5695 static void update_statusbar ( VikTrwLayer *vtl )
5697 // Get elevation data
5698 gdouble elev_gain, elev_loss;
5699 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5701 /* Find out actual distance of current track */
5702 gdouble distance = vik_track_get_length (vtl->current_track);
5703 const gchar *str = distance_string (distance);
5705 statusbar_write (str, elev_gain, elev_loss, vtl);
5707 g_free ((gpointer)str);
5711 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5713 /* if we haven't sync'ed yet, we don't have time to do more. */
5714 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5715 GList *iter = vtl->current_track->trackpoints;
5716 new_track_move_passalong_t *passalong;
5719 while ( iter->next )
5721 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5722 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5723 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5725 /* Find out actual distance of current track */
5726 gdouble distance = vik_track_get_length (vtl->current_track);
5728 // Now add distance to where the pointer is //
5731 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5732 vik_coord_to_latlon ( &coord, &ll );
5733 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5735 // Get elevation data
5736 gdouble elev_gain, elev_loss;
5737 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5739 // Adjust elevation data (if available) for the current pointer position
5741 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5742 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5743 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5744 // Adjust elevation of last track point
5745 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5747 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5750 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5754 const gchar *str = distance_string (distance);
5756 /* offset from cursor a bit */
5759 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5760 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5762 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5764 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5765 passalong->vtl = vtl;
5766 passalong->vvp = vvp;
5769 passalong->x2 = event->x;
5770 passalong->y2 = event->y;
5773 passalong->str = str;
5775 // Update statusbar with full gain/loss information
5776 statusbar_write (str, elev_gain, elev_loss, vtl);
5778 /* this will sync and undraw when we have time to */
5779 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5780 vtl->ct_sync_done = FALSE;
5781 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5783 return VIK_LAYER_TOOL_ACK;
5786 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5788 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5789 vtl->current_track = NULL;
5790 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5792 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5794 if ( vtl->current_track->trackpoints )
5796 GList *last = g_list_last(vtl->current_track->trackpoints);
5797 g_free ( last->data );
5798 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5801 update_statusbar ( vtl );
5803 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5809 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5813 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5816 if ( event->button == 3 && vtl->current_track )
5819 if ( vtl->current_track->trackpoints )
5821 GList *last = g_list_last(vtl->current_track->trackpoints);
5822 g_free ( last->data );
5823 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5825 update_statusbar ( vtl );
5827 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5831 if ( event->type == GDK_2BUTTON_PRESS )
5833 /* subtract last (duplicate from double click) tp then end */
5834 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5836 GList *last = g_list_last(vtl->current_track->trackpoints);
5837 g_free ( last->data );
5838 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5839 /* undo last, then end */
5840 vtl->current_track = NULL;
5842 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5846 if ( ! vtl->current_track )
5848 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5849 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5851 vtl->current_track = vik_track_new();
5852 vtl->current_track->visible = TRUE;
5853 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5855 /* incase it was created by begin track */
5856 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5861 tp = vik_trackpoint_new();
5862 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5864 /* snap to other TP */
5865 if ( event->state & GDK_CONTROL_MASK )
5867 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5869 tp->coord = other_tp->coord;
5872 tp->newsegment = FALSE;
5873 tp->has_timestamp = FALSE;
5875 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5876 /* Auto attempt to get elevation from DEM data (if it's available) */
5877 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5879 vtl->ct_x1 = vtl->ct_x2;
5880 vtl->ct_y1 = vtl->ct_y2;
5881 vtl->ct_x2 = event->x;
5882 vtl->ct_y2 = event->y;
5884 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5888 /*** New waypoint ****/
5890 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5895 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5898 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5900 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5901 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5902 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5907 /*** Edit trackpoint ****/
5909 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5911 tool_ed_t *t = g_new(tool_ed_t, 1);
5917 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5919 tool_ed_t *t = data;
5920 VikViewport *vvp = t->vvp;
5921 TPSearchParams params;
5922 /* OUTDATED DOCUMENTATION:
5923 find 5 pixel range on each side. then put these UTM, and a pointer
5924 to the winning track name (and maybe the winning track itself), and a
5925 pointer to the winning trackpoint, inside an array or struct. pass
5926 this along, do a foreach on the tracks which will do a foreach on the
5929 params.x = event->x;
5930 params.y = event->y;
5931 params.closest_track_id = NULL;
5932 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5933 params.closest_tp = NULL;
5935 if ( event->button != 1 )
5938 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5941 if ( !vtl->vl.visible || !vtl->tracks_visible )
5944 if ( vtl->current_tpl )
5946 /* 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.) */
5947 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5948 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
5953 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5955 if ( current_tr->visible &&
5956 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5957 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5958 marker_begin_move ( t, event->x, event->y );
5962 vtl->last_tpl = vtl->current_tpl;
5963 vtl->last_tp_track = vtl->current_tp_track;
5966 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5968 if ( params.closest_tp )
5970 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
5971 vtl->current_tpl = params.closest_tpl;
5972 vtl->current_tp_id = params.closest_track_id;
5973 trw_layer_tpwin_init ( vtl );
5974 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5975 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5979 /* these aren't the droids you're looking for */
5983 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5985 tool_ed_t *t = data;
5986 VikViewport *vvp = t->vvp;
5988 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5994 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5997 if ( event->state & GDK_CONTROL_MASK )
5999 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6000 if ( tp && tp != vtl->current_tpl->data )
6001 new_coord = tp->coord;
6003 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6006 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6007 marker_moveto ( t, x, y );
6015 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6017 tool_ed_t *t = data;
6018 VikViewport *vvp = t->vvp;
6020 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6022 if ( event->button != 1)
6027 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6030 if ( event->state & GDK_CONTROL_MASK )
6032 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6033 if ( tp && tp != vtl->current_tpl->data )
6034 new_coord = tp->coord;
6037 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6039 marker_end_move ( t );
6041 /* diff dist is diff from orig */
6043 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6044 /* can't join with itself! */
6045 trw_layer_cancel_last_tp ( vtl );
6047 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6054 /*** Route Finder ***/
6055 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6060 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6063 if ( !vtl ) return FALSE;
6064 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6065 if ( event->button == 3 && vtl->route_finder_current_track ) {
6067 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6069 vtl->route_finder_coord = *new_end;
6071 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6072 /* remove last ' to:...' */
6073 if ( vtl->route_finder_current_track->comment ) {
6074 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6075 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6076 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6077 last_to - vtl->route_finder_current_track->comment - 1);
6078 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6083 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6084 struct LatLon start, end;
6085 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6086 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6089 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6090 vik_coord_to_latlon ( &(tmp), &end );
6091 vtl->route_finder_coord = tmp; /* for continuations */
6093 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6094 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6095 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
6097 vtl->route_finder_check_added_track = TRUE;
6098 vtl->route_finder_started = FALSE;
6101 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6102 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6103 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6104 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6105 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6106 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
6109 /* see if anything was done -- a track was added or appended to */
6110 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6111 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 ) );
6112 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6113 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6114 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6115 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6117 vtl->route_finder_added_track = NULL;
6118 vtl->route_finder_check_added_track = FALSE;
6119 vtl->route_finder_append = FALSE;
6121 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6123 vtl->route_finder_started = TRUE;
6124 vtl->route_finder_coord = tmp;
6125 vtl->route_finder_current_track = NULL;
6130 /*** Show picture ****/
6132 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6137 /* Params are: vvp, event, last match found or NULL */
6138 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6140 if ( wp->image && wp->visible )
6142 gint x, y, slackx, slacky;
6143 GdkEventButton *event = (GdkEventButton *) params[1];
6145 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6146 slackx = wp->image_width / 2;
6147 slacky = wp->image_height / 2;
6148 if ( x <= event->x + slackx && x >= event->x - slackx
6149 && y <= event->y + slacky && y >= event->y - slacky )
6151 params[2] = wp->image; /* we've found a match. however continue searching
6152 * since we want to find the last match -- that
6153 * is, the match that was drawn last. */
6158 static void trw_layer_show_picture ( gpointer pass_along[6] )
6160 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6162 ShellExecute(NULL, NULL, (char *) pass_along[5], NULL, ".\\", 0);
6165 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6166 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6167 g_free ( quoted_file );
6168 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6170 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() );
6171 g_error_free ( err );
6174 #endif /* WINDOWS */
6177 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6179 gpointer params[3] = { vvp, event, NULL };
6180 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6182 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6185 static gpointer pass_along[6];
6186 pass_along[0] = vtl;
6187 pass_along[5] = params[2];
6188 trw_layer_show_picture ( pass_along );
6189 return TRUE; /* found a match */
6192 return FALSE; /* go through other layers, searching for a match */
6195 /***************************************************************************
6197 ***************************************************************************/
6203 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6205 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6206 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6209 /* Structure for thumbnail creating data used in the background thread */
6211 VikTrwLayer *vtl; // Layer needed for redrawing
6212 GSList *pics; // Image list
6213 } thumbnail_create_thread_data;
6215 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6217 guint total = g_slist_length(tctd->pics), done = 0;
6218 while ( tctd->pics )
6220 a_thumbnails_create ( (gchar *) tctd->pics->data );
6221 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6223 return -1; /* Abort thread */
6225 tctd->pics = tctd->pics->next;
6228 // Redraw to show the thumbnails as they are now created
6229 if ( IS_VIK_LAYER(tctd->vtl) )
6230 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6235 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6237 while ( tctd->pics )
6239 g_free ( tctd->pics->data );
6240 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6245 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6247 if ( ! vtl->has_verified_thumbnails )
6249 GSList *pics = NULL;
6250 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6253 gint len = g_slist_length ( pics );
6254 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6255 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6258 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6260 (vik_thr_func) create_thumbnails_thread,
6262 (vik_thr_free_func) thumbnail_create_thread_free,
6270 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6272 return vtl->coord_mode;
6277 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
6279 vik_coord_convert ( &(wp->coord), *dest_mode );
6282 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6284 vik_track_convert ( tr, *dest_mode );
6287 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6289 if ( vtl->coord_mode != dest_mode )
6291 vtl->coord_mode = dest_mode;
6292 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6293 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6297 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
6299 vtl->menu_selection = selection;
6302 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
6304 return (vtl->menu_selection);
6307 /* ----------- Downloading maps along tracks --------------- */
6309 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6311 /* TODO: calculating based on current size of viewport */
6312 const gdouble w_at_zoom_0_125 = 0.0013;
6313 const gdouble h_at_zoom_0_125 = 0.0011;
6314 gdouble zoom_factor = zoom_level/0.125;
6316 wh->lat = h_at_zoom_0_125 * zoom_factor;
6317 wh->lon = w_at_zoom_0_125 * zoom_factor;
6319 return 0; /* all OK */
6322 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6324 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6325 (dist->lat >= ABS(to->north_south - from->north_south)))
6328 VikCoord *coord = g_malloc(sizeof(VikCoord));
6329 coord->mode = VIK_COORD_LATLON;
6331 if (ABS(gradient) < 1) {
6332 if (from->east_west > to->east_west)
6333 coord->east_west = from->east_west - dist->lon;
6335 coord->east_west = from->east_west + dist->lon;
6336 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
6338 if (from->north_south > to->north_south)
6339 coord->north_south = from->north_south - dist->lat;
6341 coord->north_south = from->north_south + dist->lat;
6342 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
6348 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
6350 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
6351 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
6353 VikCoord *next = from;
6355 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
6357 list = g_list_prepend(list, next);
6363 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
6365 typedef struct _Rect {
6370 #define GLRECT(iter) ((Rect *)((iter)->data))
6373 GList *rects_to_download = NULL;
6376 if (get_download_area_width(vvp, zoom_level, &wh))
6379 GList *iter = tr->trackpoints;
6383 gboolean new_map = TRUE;
6384 VikCoord *cur_coord, tl, br;
6387 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
6389 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6390 rect = g_malloc(sizeof(Rect));
6393 rect->center = *cur_coord;
6394 rects_to_download = g_list_prepend(rects_to_download, rect);
6399 gboolean found = FALSE;
6400 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6401 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
6412 GList *fillins = NULL;
6413 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
6414 /* seems that ATM the function get_next_coord works only for LATLON */
6415 if ( cur_coord->mode == VIK_COORD_LATLON ) {
6416 /* fill-ins for far apart points */
6417 GList *cur_rect, *next_rect;
6418 for (cur_rect = rects_to_download;
6419 (next_rect = cur_rect->next) != NULL;
6420 cur_rect = cur_rect->next) {
6421 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
6422 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
6423 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
6427 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
6430 GList *iter = fillins;
6432 cur_coord = (VikCoord *)(iter->data);
6433 vik_coord_set_area(cur_coord, &wh, &tl, &br);
6434 rect = g_malloc(sizeof(Rect));
6437 rect->center = *cur_coord;
6438 rects_to_download = g_list_prepend(rects_to_download, rect);
6443 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6444 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
6448 for (iter = fillins; iter; iter = iter->next)
6450 g_list_free(fillins);
6452 if (rects_to_download) {
6453 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
6454 g_free(rect_iter->data);
6455 g_list_free(rects_to_download);
6459 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6462 gint selected_map, default_map;
6463 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6464 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6465 gint selected_zoom, default_zoom;
6469 VikTrwLayer *vtl = pass_along[0];
6470 VikLayersPanel *vlp = pass_along[1];
6471 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6472 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6474 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6475 int num_maps = g_list_length(vmls);
6478 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6482 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6483 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6485 gchar **np = map_names;
6486 VikMapsLayer **lp = map_layers;
6487 for (i = 0; i < num_maps; i++) {
6488 gboolean dup = FALSE;
6489 vml = (VikMapsLayer *)(vmls->data);
6490 for (j = 0; j < i; j++) { /* no duplicate allowed */
6491 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6498 *np++ = vik_maps_layer_get_map_label(vml);
6504 num_maps = lp - map_layers;
6506 for (default_map = 0; default_map < num_maps; default_map++) {
6507 /* TODO: check for parent layer's visibility */
6508 if (VIK_LAYER(map_layers[default_map])->visible)
6511 default_map = (default_map == num_maps) ? 0 : default_map;
6513 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6514 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6515 if (cur_zoom == zoom_vals[default_zoom])
6518 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6520 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6523 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6526 for (i = 0; i < num_maps; i++)
6527 g_free(map_names[i]);
6535 /**** lowest waypoint number calculation ***/
6536 static gint highest_wp_number_name_to_number(const gchar *name) {
6537 if ( strlen(name) == 3 ) {
6539 if ( n < 100 && name[0] != '0' )
6541 if ( n < 10 && name[0] != '0' )
6549 static void highest_wp_number_reset(VikTrwLayer *vtl)
6551 vtl->highest_wp_number = -1;
6554 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6556 /* if is bigger that top, add it */
6557 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6558 if ( new_wp_num > vtl->highest_wp_number )
6559 vtl->highest_wp_number = new_wp_num;
6562 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6564 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6565 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6566 if ( vtl->highest_wp_number == old_wp_num ) {
6568 vtl->highest_wp_number--;
6570 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6571 /* search down until we find something that *does* exist */
6573 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
6574 vtl->highest_wp_number--;
6575 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6580 /* get lowest unused number */
6581 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6584 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6586 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6587 return g_strdup(buf);