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>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #define WAYPOINT_FONT "Sans 8"
27 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
28 /* viktrwlayer.c -- 2200 lines can make a difference in the state of things */
35 #include "vikmapslayer.h"
36 #include "viktrwlayer_tpwin.h"
37 #include "viktrwlayer_propwin.h"
38 #include "garminsymbols.h"
39 #include "thumbnails.h"
40 #include "background.h"
45 #include "geonamessearch.h"
46 #ifdef VIK_CONFIG_OPENSTREETMAP
47 #include "osm-traces.h"
52 #include "icons/icons.h"
66 #include <gdk/gdkkeysyms.h>
68 #include <glib/gstdio.h>
69 #include <glib/gi18n.h>
71 /* Relax some dependencies */
72 #if ! GLIB_CHECK_VERSION(2,12,0)
73 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
74 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
78 #define VIK_TRW_LAYER_TRACK_GC 13
79 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
80 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
81 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
82 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
84 #define DRAWMODE_BY_TRACK 0
85 #define DRAWMODE_BY_VELOCITY 1
86 #define DRAWMODE_ALL_BLACK 2
91 /* this is how it knows when you click if you are clicking close to a trackpoint. */
92 #define TRACKPOINT_SIZE_APPROX 5
93 #define WAYPOINT_SIZE_APPROX 5
95 #define MIN_STOP_LENGTH 15
96 #define MAX_STOP_LENGTH 86400
97 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
98 /* this is multiplied by user-inputted value from 1-100. */
100 VIK_TRW_LAYER_SUBLAYER_TRACKS,
101 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
102 VIK_TRW_LAYER_SUBLAYER_TRACK,
103 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
106 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
108 struct _VikTrwLayer {
111 GHashTable *tracks_iters;
112 GHashTable *waypoints_iters;
113 GHashTable *waypoints;
114 GtkTreeIter waypoints_iter, tracks_iter;
115 gboolean tracks_visible, waypoints_visible;
118 guint8 drawelevation;
119 guint8 elevation_factor;
123 guint8 line_thickness;
124 guint8 bg_line_thickness;
128 gboolean wp_draw_symbols;
130 gdouble velocity_min, velocity_max;
132 guint16 track_gc_iter;
133 GdkGC *current_track_gc;
136 GdkGC *waypoint_text_gc;
137 GdkGC *waypoint_bg_gc;
138 GdkFont *waypoint_font;
139 VikTrack *current_track;
140 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
141 gboolean ct_sync_done;
144 VikCoordMode coord_mode;
146 /* wp editing tool */
147 VikWaypoint *current_wp;
148 gchar *current_wp_name;
150 gboolean waypoint_rightclick;
152 /* track editing tool */
154 gchar *current_tp_track_name;
155 VikTrwLayerTpwin *tpwin;
157 /* weird hack for joining tracks */
159 gchar *last_tp_track_name;
161 /* track editing tool -- more specifically, moving tps */
164 /* magic scissors tool */
165 gboolean magic_scissors_started;
166 VikCoord magic_scissors_coord;
167 gboolean magic_scissors_check_added_track;
168 gchar *magic_scissors_added_track_name;
169 VikTrack *magic_scissors_current_track;
170 gboolean magic_scissors_append;
177 guint16 image_cache_size;
179 /* for waypoint text */
180 PangoLayout *wplabellayout;
182 gboolean has_verified_thumbnails;
184 GtkMenu *wp_right_click_menu;
185 GtkMenu *track_right_click_menu;
188 VikStdLayerMenuItem menu_selection;
190 gint highest_wp_number;
193 /* A caached waypoint image. */
196 gchar *image; /* filename */
199 struct DrawingParams {
203 guint16 width, height;
204 const VikCoord *center;
206 gboolean one_zone, lat_lon;
207 gdouble ce1, ce2, cn1, cn2;
210 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16);
211 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl);
213 static void trw_layer_delete_item ( gpointer pass_along[6] );
214 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
215 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
217 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
218 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
219 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
221 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
222 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
224 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
225 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
226 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
228 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
229 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
231 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
232 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
233 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
234 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
235 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
236 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
237 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
238 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
239 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
240 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
241 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
242 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
243 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
244 static void trw_layer_show_picture ( gpointer pass_along[6] );
246 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
247 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
248 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
249 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
250 static void trw_layer_new_wp ( gpointer lav[2] );
251 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
252 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
253 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
254 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
255 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
256 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
257 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
258 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
261 static void trw_layer_properties_item ( gpointer pass_along[6] );
262 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
263 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
265 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
266 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
267 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
270 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
271 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
273 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
274 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
276 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
277 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
279 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
280 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
281 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
282 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
284 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
285 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
286 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
287 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
289 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
290 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
291 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
292 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
293 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
295 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
296 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
297 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
298 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
299 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
300 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
301 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
302 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
303 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
304 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
305 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
306 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
307 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
308 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
309 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
310 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
311 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
312 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
313 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
314 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
317 static void cached_pixbuf_free ( CachedPixbuf *cp );
318 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
319 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
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 void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
326 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
327 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
328 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
330 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
331 static void highest_wp_number_reset(VikTrwLayer *vtl);
332 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
333 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
336 static VikToolInterface trw_layer_tools[] = {
337 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
338 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
340 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
341 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
342 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
344 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
345 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
347 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
348 (VikToolMouseFunc) tool_edit_waypoint_click,
349 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
350 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
352 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
353 (VikToolMouseFunc) tool_edit_trackpoint_click,
354 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
355 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
357 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
358 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
360 { N_("Route Finder"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
361 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
363 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
365 /****** PARAMETERS ******/
367 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
368 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
370 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
371 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
374 static VikLayerParamScale params_scales[] = {
375 /* min max step digits */
376 { 1, 10, 1, 0 }, /* line_thickness */
377 { 0.0, 99.0, 1, 2 }, /* velocity_min */
378 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
379 /* 5 * step == how much to turn */
380 { 16, 128, 3.2, 0 }, /* image_size */
381 { 0, 255, 5, 0 }, /* image alpha */
382 { 5, 500, 5, 0 }, /* image cache_size */
383 { 0, 8, 1, 0 }, /* image cache_size */
384 { 1, 64, 1, 0 }, /* wpsize */
385 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
386 { 1, 100, 1, 0 }, /* stop_length */
389 VikLayerParam trw_layer_params[] = {
390 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
391 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
393 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
394 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
395 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
396 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
397 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
399 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
400 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
402 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
403 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
404 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
405 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
406 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
408 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
409 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
410 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
411 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
412 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
413 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
414 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
415 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
417 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
418 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
419 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
420 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
423 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 };
426 *** 1) Add to trw_layer_params and enumeration
427 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
430 /****** END PARAMETERS ******/
432 VikLayerInterface vik_trw_layer_interface = {
437 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
441 params_groups, /* params_groups */
442 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
446 (VikLayerFuncCreate) vik_trw_layer_create,
447 (VikLayerFuncRealize) vik_trw_layer_realize,
448 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
449 (VikLayerFuncFree) vik_trw_layer_free,
451 (VikLayerFuncProperties) NULL,
452 (VikLayerFuncDraw) vik_trw_layer_draw,
453 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
455 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
456 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
458 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
459 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
461 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
462 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
463 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
464 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
465 (VikLayerFuncLayerSelected) vik_trw_layer_selected,
467 (VikLayerFuncMarshall) trw_layer_marshall,
468 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
470 (VikLayerFuncSetParam) trw_layer_set_param,
471 (VikLayerFuncGetParam) trw_layer_get_param,
473 (VikLayerFuncReadFileData) a_gpspoint_read_file,
474 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
476 (VikLayerFuncDeleteItem) trw_layer_del_item,
477 (VikLayerFuncCutItem) trw_layer_cut_item,
478 (VikLayerFuncCopyItem) trw_layer_copy_item,
479 (VikLayerFuncPasteItem) trw_layer_paste_item,
480 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
482 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
484 (VikLayerFuncSelectClick) trw_layer_select_click,
485 (VikLayerFuncSelectMove) trw_layer_select_move,
486 (VikLayerFuncSelectRelease) trw_layer_select_release,
487 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
490 /* for copy & paste (I think?) */
498 GType vik_trw_layer_get_type ()
500 static GType vtl_type = 0;
504 static const GTypeInfo vtl_info =
506 sizeof (VikTrwLayerClass),
507 NULL, /* base_init */
508 NULL, /* base_finalize */
509 NULL, /* class init */
510 NULL, /* class_finalize */
511 NULL, /* class_data */
512 sizeof (VikTrwLayer),
514 NULL /* instance init */
516 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
522 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
524 static gpointer pass_along[6];
530 pass_along[1] = NULL;
531 pass_along[2] = GINT_TO_POINTER (subtype);
532 pass_along[3] = sublayer;
533 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
534 pass_along[5] = NULL;
536 trw_layer_delete_item ( pass_along );
539 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
541 static gpointer pass_along[6];
547 pass_along[1] = NULL;
548 pass_along[2] = GINT_TO_POINTER (subtype);
549 pass_along[3] = sublayer;
550 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
551 pass_along[5] = NULL;
553 trw_layer_copy_item_cb(pass_along);
554 trw_layer_cut_item_cb(pass_along);
557 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
559 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
560 gint subtype = GPOINTER_TO_INT (pass_along[2]);
561 gpointer * sublayer = pass_along[3];
565 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
568 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
573 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
575 trw_layer_copy_item_cb(pass_along);
576 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
577 trw_layer_delete_item(pass_along);
580 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
591 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
593 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
595 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
598 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
599 fi = g_malloc ( *len );
600 fi->len = strlen(sublayer) + 1;
601 memcpy(fi->data, sublayer, fi->len);
602 memcpy(fi->data + fi->len, id, il);
604 *item = (guint8 *)fi;
607 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
609 FlatItem *fi = (FlatItem *) item;
611 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
616 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
617 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
618 vik_trw_layer_add_waypoint ( vtl, name, w );
619 waypoint_convert(name, w, &vtl->coord_mode);
620 // Consider if redraw necessary for the new item
621 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
622 vik_layer_emit_update ( VIK_LAYER(vtl) );
625 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
629 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
630 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
631 vik_trw_layer_add_track ( vtl, name, t );
632 track_convert(name, t, &vtl->coord_mode);
633 // Consider if redraw necessary for the new item
634 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
635 vik_layer_emit_update ( VIK_LAYER(vtl) );
641 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
648 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
652 case PARAM_TV: vtl->tracks_visible = data.b; break;
653 case PARAM_WV: vtl->waypoints_visible = data.b; break;
654 case PARAM_DM: vtl->drawmode = data.u; break;
655 case PARAM_DP: vtl->drawpoints = data.b; break;
656 case PARAM_DE: vtl->drawelevation = data.b; break;
657 case PARAM_DS: vtl->drawstops = data.b; break;
658 case PARAM_DL: vtl->drawlines = data.b; break;
659 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
660 vtl->stop_length = data.u;
662 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
663 vtl->elevation_factor = data.u;
665 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
667 vtl->line_thickness = data.u;
668 trw_layer_new_track_gcs ( vtl, vp );
671 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
673 vtl->bg_line_thickness = data.u;
674 trw_layer_new_track_gcs ( vtl, vp );
679 /* Convert to store internally
680 NB file operation always in internal units (metres per second) */
681 vik_units_speed_t speed_units = a_vik_get_units_speed ();
682 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
683 vtl->velocity_min = data.d;
684 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
685 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
686 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
687 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
690 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
695 /* Convert to store internally
696 NB file operation always in internal units (metres per second) */
697 vik_units_speed_t speed_units = a_vik_get_units_speed ();
698 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
699 vtl->velocity_max = data.d;
700 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
701 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
702 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
703 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
706 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
709 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
710 case PARAM_DLA: vtl->drawlabels = data.b; break;
711 case PARAM_DI: vtl->drawimages = data.b; break;
712 case PARAM_IS: if ( data.u != vtl->image_size )
714 vtl->image_size = data.u;
715 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
716 g_queue_free ( vtl->image_cache );
717 vtl->image_cache = g_queue_new ();
720 case PARAM_IA: vtl->image_alpha = data.u; break;
721 case PARAM_ICS: vtl->image_cache_size = data.u;
722 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
723 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
725 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
726 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
727 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
728 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
729 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
730 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
731 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
736 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
738 VikLayerParamData rv;
741 case PARAM_TV: rv.b = vtl->tracks_visible; break;
742 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
743 case PARAM_DM: rv.u = vtl->drawmode; break;
744 case PARAM_DP: rv.b = vtl->drawpoints; break;
745 case PARAM_DE: rv.b = vtl->drawelevation; break;
746 case PARAM_EF: rv.u = vtl->elevation_factor; break;
747 case PARAM_DS: rv.b = vtl->drawstops; break;
748 case PARAM_SL: rv.u = vtl->stop_length; break;
749 case PARAM_DL: rv.b = vtl->drawlines; break;
750 case PARAM_LT: rv.u = vtl->line_thickness; break;
751 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
754 /* Convert to store internally
755 NB file operation always in internal units (metres per second) */
756 vik_units_speed_t speed_units = a_vik_get_units_speed ();
757 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
758 rv.d = vtl->velocity_min;
759 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
760 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
761 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
762 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
765 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
770 /* Convert to store internally
771 NB file operation always in internal units (metres per second) */
772 vik_units_speed_t speed_units = a_vik_get_units_speed ();
773 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
774 rv.d = vtl->velocity_max;
775 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
776 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
777 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
778 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
781 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
784 case PARAM_DLA: rv.b = vtl->drawlabels; break;
785 case PARAM_DI: rv.b = vtl->drawimages; break;
786 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
787 case PARAM_IS: rv.u = vtl->image_size; break;
788 case PARAM_IA: rv.u = vtl->image_alpha; break;
789 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
790 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
791 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
792 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
793 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
794 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
795 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
796 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
801 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
812 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
813 a_gpx_write_file(vtl, f);
814 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
817 g_file_get_contents(tmpname, &dd, &dl, NULL);
818 *len = sizeof(pl) + pl + dl;
819 *data = g_malloc(*len);
820 memcpy(*data, &pl, sizeof(pl));
821 memcpy(*data + sizeof(pl), pd, pl);
822 memcpy(*data + sizeof(pl) + pl, dd, dl);
831 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
833 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
839 memcpy(&pl, data, sizeof(pl));
841 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
844 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
845 g_critical("couldn't open temp file");
848 fwrite(data, len - pl - sizeof(pl), 1, f);
850 a_gpx_read_file(rv, f);
858 static GList * str_array_to_glist(gchar* data[])
862 for (p = (gpointer)data; *p; p++)
863 gl = g_list_prepend(gl, *p);
864 return(g_list_reverse(gl));
867 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
869 return (strcasecmp(s1, s2) == 0);
872 static guint strcase_hash(gconstpointer v)
874 /* 31 bit hash function */
877 gchar s[128]; /* malloc is too slow for reading big files */
880 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
881 p[i] = toupper(t[i]);
887 for (p += 1; *p != '\0'; p++)
888 h = (h << 5) - h + *p;
894 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
896 if (trw_layer_params[PARAM_DM].widget_data == NULL)
897 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
898 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
899 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
901 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
902 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
904 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
905 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
906 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
907 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
909 /* TODO: constants at top */
910 rv->waypoints_visible = rv->tracks_visible = TRUE;
911 rv->drawmode = drawmode;
912 rv->drawpoints = TRUE;
913 rv->drawstops = FALSE;
914 rv->drawelevation = FALSE;
915 rv->elevation_factor = 30;
916 rv->stop_length = 60;
917 rv->drawlines = TRUE;
918 rv->wplabellayout = NULL;
919 rv->wp_right_click_menu = NULL;
920 rv->track_right_click_menu = NULL;
921 rv->waypoint_gc = NULL;
922 rv->waypoint_text_gc = NULL;
923 rv->waypoint_bg_gc = NULL;
925 rv->velocity_max = 5.0;
926 rv->velocity_min = 0.0;
927 rv->line_thickness = 1;
928 rv->bg_line_thickness = 0;
929 rv->current_wp = NULL;
930 rv->current_wp_name = NULL;
931 rv->current_track = NULL;
932 rv->current_tpl = NULL;
933 rv->current_tp_track_name = NULL;
934 rv->moving_tp = FALSE;
935 rv->moving_wp = FALSE;
937 rv->ct_sync_done = TRUE;
939 rv->magic_scissors_started = FALSE;
940 rv->magic_scissors_check_added_track = FALSE;
941 rv->magic_scissors_added_track_name = NULL;
942 rv->magic_scissors_current_track = NULL;
943 rv->magic_scissors_append = FALSE;
945 rv->waypoint_rightclick = FALSE;
947 rv->last_tp_track_name = NULL;
949 rv->image_cache = g_queue_new();
951 rv->image_alpha = 255;
952 rv->image_cache_size = 300;
953 rv->drawimages = TRUE;
954 rv->drawlabels = TRUE;
959 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
961 g_hash_table_destroy(trwlayer->waypoints);
962 g_hash_table_destroy(trwlayer->tracks);
964 /* ODC: replace with GArray */
965 trw_layer_free_track_gcs ( trwlayer );
967 if ( trwlayer->wp_right_click_menu )
968 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
970 if ( trwlayer->track_right_click_menu )
971 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
973 if ( trwlayer->wplabellayout != NULL)
974 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
976 if ( trwlayer->waypoint_gc != NULL )
977 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
979 if ( trwlayer->waypoint_text_gc != NULL )
980 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
982 if ( trwlayer->waypoint_bg_gc != NULL )
983 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
985 if ( trwlayer->waypoint_font != NULL )
986 gdk_font_unref ( trwlayer->waypoint_font );
988 if ( trwlayer->tpwin != NULL )
989 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
991 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
992 g_queue_free ( trwlayer->image_cache );
995 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
998 dp->xmpp = vik_viewport_get_xmpp ( vp );
999 dp->ympp = vik_viewport_get_ympp ( vp );
1000 dp->width = vik_viewport_get_width ( vp );
1001 dp->height = vik_viewport_get_height ( vp );
1002 dp->center = vik_viewport_get_center ( vp );
1003 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1004 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1009 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1010 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1011 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1013 dp->ce1 = dp->center->east_west-w2;
1014 dp->ce2 = dp->center->east_west+w2;
1015 dp->cn1 = dp->center->north_south-h2;
1016 dp->cn2 = dp->center->north_south+h2;
1017 } else if ( dp->lat_lon ) {
1018 VikCoord upperleft, bottomright;
1019 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1020 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1021 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1022 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1023 dp->ce1 = upperleft.east_west;
1024 dp->ce2 = bottomright.east_west;
1025 dp->cn1 = bottomright.north_south;
1026 dp->cn2 = upperleft.north_south;
1029 dp->track_gc_iter = 0;
1032 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1034 static gdouble rv = 0;
1035 if ( tp1->has_timestamp && tp2->has_timestamp )
1037 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1038 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1041 return VIK_TRW_LAYER_TRACK_GC_MIN;
1042 else if ( vtl->velocity_min >= vtl->velocity_max )
1043 return VIK_TRW_LAYER_TRACK_GC_MAX;
1045 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1047 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1048 return VIK_TRW_LAYER_TRACK_GC_MAX;
1052 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1055 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1057 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1058 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1059 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1060 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1063 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1065 /* TODO: this function is a mess, get rid of any redundancy */
1066 GList *list = track->trackpoints;
1068 gboolean useoldvals = TRUE;
1070 gboolean drawpoints;
1072 gboolean drawelevation;
1073 gdouble min_alt, max_alt, alt_diff = 0;
1075 const guint8 tp_size_reg = 2;
1076 const guint8 tp_size_cur = 4;
1079 if ( dp->vtl->drawelevation )
1081 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1082 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1083 alt_diff = max_alt - min_alt;
1086 if ( ! track->visible )
1089 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1090 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1091 trw_layer_draw_track ( name, track, dp, TRUE );
1093 if ( drawing_white_background )
1094 drawpoints = drawstops = FALSE;
1096 drawpoints = dp->vtl->drawpoints;
1097 drawstops = dp->vtl->drawstops;
1100 /* Current track - used for creation */
1101 if ( track == dp->vtl->current_track )
1102 main_gc = dp->vtl->current_track_gc;
1104 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1105 /* Draw all tracks of the layer in special colour */
1106 /* if track is member of selected layer or is the current selected track
1107 then draw in the highlight colour.
1108 NB this supercedes the drawmode */
1109 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1110 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1111 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1112 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1115 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1116 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1118 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1122 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1123 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1125 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1130 int x, y, oldx, oldy;
1131 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1133 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1135 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1137 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1139 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1140 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1146 while ((list = g_list_next(list)))
1148 tp = VIK_TRACKPOINT(list->data);
1149 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1151 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1152 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1153 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1154 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1155 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1157 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1159 if ( drawpoints && ! drawing_white_background )
1162 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1165 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1166 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 );
1169 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 );
1172 if ((!tp->newsegment) && (dp->vtl->drawlines))
1174 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1176 /* UTM only: zone check */
1177 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1178 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1180 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1181 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1182 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1186 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1188 if ( drawing_white_background ) {
1189 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1193 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1194 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1196 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1197 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1201 tmp[1].y = oldy-FIXALTITUDE(list->data);
1203 tmp[2].y = y-FIXALTITUDE(list->next->data);
1208 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1209 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1211 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1212 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1214 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1224 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1226 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1227 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1229 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1230 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1231 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1232 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1235 if ( drawing_white_background )
1236 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1238 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1242 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1243 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1250 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1251 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1252 dp->track_gc_iter = 0;
1255 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1256 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1258 trw_layer_draw_track ( name, track, dp, FALSE );
1261 static void cached_pixbuf_free ( CachedPixbuf *cp )
1263 g_object_unref ( G_OBJECT(cp->pixbuf) );
1264 g_free ( cp->image );
1267 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1269 return strcmp ( cp->image, name );
1272 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1275 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1276 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1277 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1280 GdkPixbuf *sym = NULL;
1281 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1283 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1285 if ( wp->image && dp->vtl->drawimages )
1287 GdkPixbuf *pixbuf = NULL;
1290 if ( dp->vtl->image_alpha == 0)
1293 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1295 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1298 gchar *image = wp->image;
1299 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1300 if ( ! regularthumb )
1302 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1303 image = "\x12\x00"; /* this shouldn't occur naturally. */
1307 CachedPixbuf *cp = NULL;
1308 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1309 if ( dp->vtl->image_size == 128 )
1310 cp->pixbuf = regularthumb;
1313 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1314 g_assert ( cp->pixbuf );
1315 g_object_unref ( G_OBJECT(regularthumb) );
1317 cp->image = g_strdup ( image );
1319 /* needed so 'click picture' tool knows how big the pic is; we don't
1320 * store it in cp because they may have been freed already. */
1321 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1322 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1324 g_queue_push_head ( dp->vtl->image_cache, cp );
1325 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1326 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1328 pixbuf = cp->pixbuf;
1332 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1338 w = gdk_pixbuf_get_width ( pixbuf );
1339 h = gdk_pixbuf_get_height ( pixbuf );
1341 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1343 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1344 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1345 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1346 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1347 // Highlighted - so draw a little border around the chosen one
1348 // single line seems a little weak so draw 2 of them
1349 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1350 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1351 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1352 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1355 if ( dp->vtl->image_alpha == 255 )
1356 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1358 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1360 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1364 /* DRAW ACTUAL DOT */
1365 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1366 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 );
1368 else if ( wp == dp->vtl->current_wp ) {
1369 switch ( dp->vtl->wp_symbol ) {
1370 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;
1371 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;
1372 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;
1373 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 );
1374 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 );
1378 switch ( dp->vtl->wp_symbol ) {
1379 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;
1380 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;
1381 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;
1382 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 );
1383 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;
1387 if ( dp->vtl->drawlabels )
1389 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1390 gint label_x, label_y;
1392 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1393 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1394 label_x = x - width/2;
1396 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1398 label_y = y - dp->vtl->wp_size - height - 2;
1400 /* if highlight mode on, then draw background text in highlight colour */
1401 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1402 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1403 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1404 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1405 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1407 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1410 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1412 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1417 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1419 static struct DrawingParams dp;
1420 g_assert ( l != NULL );
1422 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1425 if ( l->tracks_visible )
1426 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1428 if (l->waypoints_visible)
1429 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1432 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1435 if ( vtl->track_bg_gc )
1437 g_object_unref ( vtl->track_bg_gc );
1438 vtl->track_bg_gc = NULL;
1440 if ( vtl->current_track_gc )
1442 g_object_unref ( vtl->current_track_gc );
1443 vtl->current_track_gc = NULL;
1446 if ( ! vtl->track_gc )
1448 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1449 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1450 g_array_free ( vtl->track_gc, TRUE );
1451 vtl->track_gc = NULL;
1454 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1456 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1457 gint width = vtl->line_thickness;
1459 if ( vtl->track_gc )
1460 trw_layer_free_track_gcs ( vtl );
1462 if ( vtl->track_bg_gc )
1463 g_object_unref ( vtl->track_bg_gc );
1464 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1466 if ( vtl->current_track_gc )
1467 g_object_unref ( vtl->current_track_gc );
1468 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1469 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1471 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1473 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1475 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1476 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1477 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1478 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1479 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1480 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1481 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1482 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1483 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1484 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1486 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1488 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1490 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1493 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1495 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1496 PangoFontDescription *pfd;
1497 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1498 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1499 pango_layout_set_font_description (rv->wplabellayout, pfd);
1500 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1501 pango_font_description_free (pfd);
1503 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1505 trw_layer_new_track_gcs ( rv, vp );
1507 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1508 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1509 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1510 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1512 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1514 rv->has_verified_thumbnails = FALSE;
1515 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1517 rv->wp_draw_symbols = TRUE;
1519 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1521 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1526 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1528 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1530 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1531 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1533 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1536 *new_iter = *((GtkTreeIter *) pass_along[1]);
1537 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1539 if ( ! track->visible )
1540 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1543 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1545 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1546 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1547 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1549 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1552 *new_iter = *((GtkTreeIter *) pass_along[1]);
1553 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1555 if ( ! wp->visible )
1556 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1560 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1563 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1565 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1566 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1568 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1570 if ( ! vtl->tracks_visible )
1571 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1573 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1575 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1576 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1578 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1581 if ( ! vtl->waypoints_visible )
1582 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1584 pass_along[0] = &(vtl->waypoints_iter);
1585 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1587 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1591 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1595 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1596 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1597 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1599 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1601 return (t->visible ^= 1);
1605 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1607 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1609 return (t->visible ^= 1);
1617 // Structure to hold multiple track information for a layer
1626 * Build up layer multiple track information via updating the tooltip_tracks structure
1628 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1630 tt->length = tt->length + vik_track_get_length (tr);
1632 // Ensure times are available
1633 if ( tr->trackpoints &&
1634 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1635 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1638 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1639 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1641 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1642 // Hence initialize to the first 'proper' value
1643 if ( tt->start_time == 0 )
1644 tt->start_time = t1;
1645 if ( tt->end_time == 0 )
1648 // Update find the earliest / last times
1649 if ( t1 < tt->start_time )
1650 tt->start_time = t1;
1651 if ( t2 > tt->end_time )
1654 // Keep track of total time
1655 // there maybe gaps within a track (eg segments)
1656 // but this should be generally good enough for a simple indicator
1657 tt->duration = tt->duration + (int)(t2-t1);
1662 * Generate tooltip text for the layer.
1663 * This is relatively complicated as it considers information for
1664 * no tracks, a single track or multiple tracks
1665 * (which may or may not have timing information)
1667 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1678 static gchar tmp_buf[128];
1681 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1683 // Safety check - I think these should always be valid
1684 if ( vtl->tracks && vtl->waypoints ) {
1685 tooltip_tracks tt = { 0.0, 0, 0 };
1686 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1688 GDate* gdate_start = g_date_new ();
1689 g_date_set_time_t (gdate_start, tt.start_time);
1691 GDate* gdate_end = g_date_new ();
1692 g_date_set_time_t (gdate_end, tt.end_time);
1694 if ( g_date_compare (gdate_start, gdate_end) ) {
1695 // Dates differ so print range on separate line
1696 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1697 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1698 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1701 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1702 if ( tt.start_time != 0 )
1703 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1707 if ( tt.length > 0.0 ) {
1708 gdouble len_in_units;
1710 // Setup info dependent on distance units
1711 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1712 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1713 len_in_units = VIK_METERS_TO_MILES(tt.length);
1716 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1717 len_in_units = tt.length/1000.0;
1720 // Timing information if available
1722 if ( tt.duration > 0 ) {
1723 g_snprintf (tbuf1, sizeof(tbuf1),
1724 _(" in %d:%02d hrs:mins"),
1725 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1727 g_snprintf (tbuf2, sizeof(tbuf2),
1728 _("\n%sTotal Length %.1f %s%s"),
1729 tbuf3, len_in_units, tbuf4, tbuf1);
1732 // Put together all the elements to form compact tooltip text
1733 g_snprintf (tmp_buf, sizeof(tmp_buf),
1734 _("Tracks: %d - Waypoints: %d%s"),
1735 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1737 g_date_free (gdate_start);
1738 g_date_free (gdate_end);
1745 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1749 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1750 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1751 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1753 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1755 // Could be a better way of handling strings - but this works...
1756 gchar time_buf1[20];
1757 gchar time_buf2[20];
1758 time_buf1[0] = '\0';
1759 time_buf2[0] = '\0';
1760 static gchar tmp_buf[100];
1761 // Compact info: Short date eg (11/20/99), duration and length
1762 // Hopefully these are the things that are most useful and so promoted into the tooltip
1763 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1764 // %x The preferred date representation for the current locale without the time.
1765 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1766 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1767 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1769 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1772 // Get length and consider the appropriate distance units
1773 gdouble tr_len = vik_track_get_length(tr);
1774 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1775 switch (dist_units) {
1776 case VIK_UNITS_DISTANCE_KILOMETRES:
1777 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1779 case VIK_UNITS_DISTANCE_MILES:
1780 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1789 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1791 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1792 // NB It's OK to return NULL
1802 * General layer selection function, find out which bit is selected and take appropriate action
1804 gboolean vik_trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1807 l->current_wp = NULL;
1808 l->current_wp_name = NULL;
1809 trw_layer_cancel_current_tp ( l, FALSE );
1813 case VIK_TREEVIEW_TYPE_LAYER:
1815 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1816 /* Mark for redraw */
1821 case VIK_TREEVIEW_TYPE_SUBLAYER:
1825 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1827 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1828 /* Mark for redraw */
1832 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1834 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->tracks, sublayer ), l, sublayer );
1835 /* Mark for redraw */
1839 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1841 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
1842 /* Mark for redraw */
1846 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1848 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->waypoints, sublayer ), l, sublayer );
1849 /* Mark for redraw */
1855 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1864 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1869 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1874 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1876 return l->waypoints;
1879 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1881 static VikCoord fixme;
1882 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1883 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1884 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1885 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1886 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1887 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1888 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1889 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1890 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1893 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1896 static VikCoord fixme;
1900 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1901 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1902 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1903 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1904 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1905 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1906 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1907 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1908 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1913 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1915 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1916 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1918 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1919 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1920 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1921 maxmin[0].lat = wpt_maxmin[0].lat;
1924 maxmin[0].lat = trk_maxmin[0].lat;
1926 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1927 maxmin[0].lon = wpt_maxmin[0].lon;
1930 maxmin[0].lon = trk_maxmin[0].lon;
1932 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1933 maxmin[1].lat = wpt_maxmin[1].lat;
1936 maxmin[1].lat = trk_maxmin[1].lat;
1938 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1939 maxmin[1].lon = wpt_maxmin[1].lon;
1942 maxmin[1].lon = trk_maxmin[1].lon;
1946 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1948 /* 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... */
1949 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1950 trw_layer_find_maxmin (vtl, maxmin);
1951 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1955 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1956 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1961 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1964 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1965 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
1967 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1970 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1972 /* First set the center [in case previously viewing from elsewhere] */
1973 /* Then loop through zoom levels until provided positions are in view */
1974 /* This method is not particularly fast - but should work well enough */
1975 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1977 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1978 vik_viewport_set_center_coord ( vvp, &coord );
1980 /* Convert into definite 'smallest' and 'largest' positions */
1981 struct LatLon minmin;
1982 if ( maxmin[0].lat < maxmin[1].lat )
1983 minmin.lat = maxmin[0].lat;
1985 minmin.lat = maxmin[1].lat;
1987 struct LatLon maxmax;
1988 if ( maxmin[0].lon > maxmin[1].lon )
1989 maxmax.lon = maxmin[0].lon;
1991 maxmax.lon = maxmin[1].lon;
1993 /* Never zoom in too far - generally not that useful, as too close ! */
1994 /* Always recalculate the 'best' zoom level */
1996 vik_viewport_set_zoom ( vvp, zoom );
1998 gdouble min_lat, max_lat, min_lon, max_lon;
1999 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2000 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2001 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2002 /* NB I think the logic used in this test to determine if the bounds is within view
2003 fails if track goes across 180 degrees longitude.
2004 Hopefully that situation is not too common...
2005 Mind you viking doesn't really do edge locations to well anyway */
2006 if ( min_lat < minmin.lat &&
2007 max_lat > minmin.lat &&
2008 min_lon < maxmax.lon &&
2009 max_lon > maxmax.lon )
2010 /* Found within zoom level */
2015 vik_viewport_set_zoom ( vvp, zoom );
2019 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2021 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2022 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2023 trw_layer_find_maxmin (vtl, maxmin);
2024 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2027 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2032 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2034 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])) ) ) {
2035 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2038 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2041 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2043 GtkWidget *file_selector;
2045 gboolean failed = FALSE;
2046 file_selector = gtk_file_chooser_dialog_new (title,
2048 GTK_FILE_CHOOSER_ACTION_SAVE,
2049 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2050 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2052 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2054 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2056 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2057 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2059 gtk_widget_hide ( file_selector );
2060 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2065 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2067 gtk_widget_hide ( file_selector );
2068 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2073 gtk_widget_destroy ( file_selector );
2075 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2078 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2080 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2083 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2085 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2088 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2090 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX );
2093 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2095 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2096 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2097 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2098 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2100 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2102 g_free ( auto_save_name );
2105 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2107 gpointer layer_and_vlp[2];
2108 layer_and_vlp[0] = pass_along[0];
2109 layer_and_vlp[1] = pass_along[1];
2111 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2112 gchar *auto_save_name = g_strdup ( pass_along[3] );
2113 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2114 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2116 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2118 g_free ( auto_save_name );
2121 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2123 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2124 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2125 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2126 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2128 GTK_RESPONSE_REJECT,
2130 GTK_RESPONSE_ACCEPT,
2133 GtkWidget *label, *entry;
2134 label = gtk_label_new(_("Waypoint Name:"));
2135 entry = gtk_entry_new();
2137 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2138 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2139 gtk_widget_show_all ( label );
2140 gtk_widget_show_all ( entry );
2142 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2144 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2147 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2150 for ( i = strlen(upname)-1; i >= 0; i-- )
2151 upname[i] = toupper(upname[i]);
2153 wp = g_hash_table_lookup ( wps, upname );
2156 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2159 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2160 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2161 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, upname ), TRUE );
2168 gtk_widget_destroy ( dia );
2171 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2173 gchar *default_name = highest_wp_number_get(vtl);
2174 VikWaypoint *wp = vik_waypoint_new();
2175 gchar *returned_name;
2177 wp->coord = *def_coord;
2179 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2182 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2183 g_free (default_name);
2186 g_free (default_name);
2187 vik_waypoint_free(wp);
2191 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2194 struct LatLon one_ll, two_ll;
2195 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2197 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2198 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2199 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2200 VikViewport *vvp = vik_window_viewport(vw);
2201 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2202 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2203 vik_coord_to_latlon(&one, &one_ll);
2204 vik_coord_to_latlon(&two, &two_ll);
2205 if (one_ll.lat > two_ll.lat) {
2206 maxmin[0].lat = one_ll.lat;
2207 maxmin[1].lat = two_ll.lat;
2210 maxmin[0].lat = two_ll.lat;
2211 maxmin[1].lat = one_ll.lat;
2213 if (one_ll.lon > two_ll.lon) {
2214 maxmin[0].lon = one_ll.lon;
2215 maxmin[1].lon = two_ll.lon;
2218 maxmin[0].lon = two_ll.lon;
2219 maxmin[1].lon = one_ll.lon;
2221 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2224 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2226 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2227 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2228 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2230 trw_layer_find_maxmin (vtl, maxmin);
2231 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2234 static void trw_layer_new_wp ( gpointer lav[2] )
2236 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2237 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2238 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2239 instead return true if you want to update. */
2240 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 )
2241 vik_layers_panel_emit_update ( vlp );
2244 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2246 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2247 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2249 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2250 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2251 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2252 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2253 vik_layers_panel_emit_update ( vlp );
2257 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2259 /* NB do not care if wp is visible or not */
2260 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2263 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2265 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2266 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2268 /* Only 1 waypoint - jump straight to it */
2269 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2270 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2271 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2273 /* If at least 2 waypoints - find center and then zoom to fit */
2274 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2276 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2277 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2278 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2281 vik_layers_panel_emit_update ( vlp );
2284 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2286 static gpointer pass_along[2];
2288 GtkWidget *export_submenu;
2289 GtkWidget *wikipedia_submenu;
2290 pass_along[0] = vtl;
2291 pass_along[1] = vlp;
2293 item = gtk_menu_item_new();
2294 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2295 gtk_widget_show ( item );
2297 item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") );
2298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2299 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2300 gtk_widget_show ( item );
2302 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2303 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2304 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2305 gtk_widget_show ( item );
2307 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2308 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2309 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2310 gtk_widget_show ( item );
2312 item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2314 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2315 gtk_widget_show ( item );
2317 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2318 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2319 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2320 gtk_widget_show ( item );
2322 export_submenu = gtk_menu_new ();
2323 item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
2324 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2325 gtk_widget_show ( item );
2326 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2328 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2329 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2330 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2331 gtk_widget_show ( item );
2333 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2335 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2336 gtk_widget_show ( item );
2338 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2340 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2341 gtk_widget_show ( item );
2343 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2344 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2345 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2346 gtk_widget_show ( item );
2348 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2349 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2350 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2351 gtk_widget_show ( item );
2353 #ifdef VIK_CONFIG_GEONAMES
2354 wikipedia_submenu = gtk_menu_new();
2355 item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2356 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2357 gtk_widget_show(item);
2358 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2360 item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2361 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2362 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2363 gtk_widget_show ( item );
2365 item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
2366 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2367 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2368 gtk_widget_show ( item );
2371 #ifdef VIK_CONFIG_OPENSTREETMAP
2372 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2373 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2374 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2375 gtk_widget_show ( item );
2378 GtkWidget *delete_submenu = gtk_menu_new ();
2379 item = gtk_menu_item_new_with_mnemonic ( _("De_lete") );
2380 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2381 gtk_widget_show ( item );
2382 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2384 item = gtk_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2386 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2387 gtk_widget_show ( item );
2389 item = gtk_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2390 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2391 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2392 gtk_widget_show ( item );
2394 item = gtk_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2395 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2396 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2397 gtk_widget_show ( item );
2399 item = gtk_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2400 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2401 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2402 gtk_widget_show ( item );
2404 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2405 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2407 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2408 gtk_widget_show ( item );
2411 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2412 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2414 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2415 gtk_widget_show ( item );
2419 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2421 if ( VIK_LAYER(vtl)->realized )
2423 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2425 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2428 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2429 // Visibility column always needed for waypoints
2430 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2431 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2433 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2435 // Actual setting of visibility dependent on the waypoint
2436 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2437 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2441 highest_wp_number_add_wp(vtl, name);
2442 g_hash_table_insert ( vtl->waypoints, name, wp );
2446 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2448 if ( VIK_LAYER(vtl)->realized )
2450 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2452 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2455 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2456 // Visibility column always needed for tracks
2457 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2458 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2460 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2462 // Actual setting of visibility dependent on the track
2463 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2464 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2468 g_hash_table_insert ( vtl->tracks, name, t );
2472 /* to be called whenever a track has been deleted or may have been changed. */
2473 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2475 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2476 trw_layer_cancel_current_tp ( vtl, FALSE );
2477 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2478 trw_layer_cancel_last_tp ( vtl );
2481 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2484 gchar *newname = g_strdup(name);
2485 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2486 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2487 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2489 newname = new_newname;
2495 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2497 vik_trw_layer_add_waypoint ( vtl,
2498 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2501 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2503 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
2504 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2505 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2506 vik_track_free ( tr );
2507 vtl->magic_scissors_append = FALSE; /* this means we have added it */
2509 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2510 vik_trw_layer_add_track ( vtl, new_name, tr );
2512 if ( vtl->magic_scissors_check_added_track ) {
2513 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2514 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2515 g_free ( vtl->magic_scissors_added_track_name );
2516 vtl->magic_scissors_added_track_name = g_strdup(new_name);
2521 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2523 *l = g_list_append(*l, (gpointer)name);
2526 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2528 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2529 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2531 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2532 vik_trw_layer_delete_track(vtl_src, name);
2533 vik_trw_layer_add_track(vtl_dest, newname, t);
2535 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2537 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2538 vik_trw_layer_delete_waypoint(vtl_src, name);
2539 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2543 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2545 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2546 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2548 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2549 GList *items = NULL;
2552 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2553 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2555 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2556 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2561 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2562 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2564 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2571 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2572 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2576 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2578 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2579 gboolean was_visible = FALSE;
2583 was_visible = t->visible;
2584 if ( t == vtl->current_track ) {
2585 vtl->current_track = NULL;
2587 if ( t == vtl->magic_scissors_current_track )
2588 vtl->magic_scissors_current_track = NULL;
2589 /* could be current_tp, so we have to check */
2590 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2592 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2593 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2594 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2596 /* do this last because trk_name may be pointing to actual orig key */
2597 g_hash_table_remove ( vtl->tracks, trk_name );
2602 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2604 gboolean was_visible = FALSE;
2607 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2611 if ( wp == vtl->current_wp ) {
2612 vtl->current_wp = NULL;
2613 vtl->current_wp_name = NULL;
2614 vtl->moving_wp = FALSE;
2617 was_visible = wp->visible;
2618 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2619 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2620 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2622 highest_wp_number_remove_wp(vtl, wp_name);
2623 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2629 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2631 vik_treeview_item_delete (vt, it );
2634 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2637 vtl->current_track = NULL;
2638 vtl->magic_scissors_current_track = NULL;
2639 if (vtl->current_tp_track_name)
2640 trw_layer_cancel_current_tp(vtl, FALSE);
2641 if (vtl->last_tp_track_name)
2642 trw_layer_cancel_last_tp ( vtl );
2644 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2645 g_hash_table_remove_all(vtl->tracks_iters);
2646 g_hash_table_remove_all(vtl->tracks);
2648 /* TODO: only update if the layer is visible (ticked) */
2649 vik_layer_emit_update ( VIK_LAYER(vtl) );
2652 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2654 vtl->current_wp = NULL;
2655 vtl->current_wp_name = NULL;
2656 vtl->moving_wp = FALSE;
2658 highest_wp_number_reset(vtl);
2660 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2661 g_hash_table_remove_all(vtl->waypoints_iters);
2662 g_hash_table_remove_all(vtl->waypoints);
2664 /* TODO: only update if the layer is visible (ticked) */
2665 vik_layer_emit_update ( VIK_LAYER(vtl) );
2668 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
2670 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2671 // Get confirmation from the user
2672 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2673 _("Are you sure you want to delete all tracks in %s?"),
2674 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2675 vik_trw_layer_delete_all_tracks (vtl);
2678 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
2680 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2681 // Get confirmation from the user
2682 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2683 _("Are you sure you want to delete all waypoints in %s?"),
2684 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2685 vik_trw_layer_delete_all_waypoints (vtl);
2688 static void trw_layer_delete_item ( gpointer pass_along[6] )
2690 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2691 gboolean was_visible = FALSE;
2692 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2694 if ( GPOINTER_TO_INT ( pass_along[4]) )
2695 // Get confirmation from the user
2696 // Maybe this Waypoint Delete should be optional as is it could get annoying...
2697 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2698 _("Are you sure you want to delete the waypoint \"%s\""),
2701 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2705 if ( GPOINTER_TO_INT ( pass_along[4]) )
2706 // Get confirmation from the user
2707 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2708 _("Are you sure you want to delete the track \"%s\""),
2711 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2714 vik_layer_emit_update ( VIK_LAYER(vtl) );
2718 static void trw_layer_properties_item ( gpointer pass_along[6] )
2720 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2721 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2723 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2726 gboolean updated = FALSE;
2727 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
2729 if ( updated && VIK_LAYER(vtl)->visible )
2730 vik_layer_emit_update ( VIK_LAYER(vtl) );
2735 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2738 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2740 pass_along[1], /* vlp */
2741 pass_along[3], /* track name */
2742 pass_along[5] ); /* vvp */
2748 Parameter 1 -> VikLayersPanel
2749 Parameter 2 -> VikLayer
2750 Parameter 3 -> VikViewport
2752 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2755 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2756 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2759 /* since vlp not set, vl & vvp should be valid instead! */
2761 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2762 vik_layer_emit_update ( VIK_LAYER(vl) );
2767 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
2769 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2770 if ( trps && trps->data )
2771 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2774 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
2776 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2777 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2778 if ( trps && *trps )
2780 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2782 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2783 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2784 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2785 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2786 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
2790 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2792 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2793 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2795 vtl->current_track = track;
2796 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2798 if ( track->trackpoints )
2799 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2803 * extend a track using magic scissors
2805 static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2807 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2808 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2809 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2811 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2812 vtl->magic_scissors_coord = last_coord;
2813 vtl->magic_scissors_current_track = track;
2814 vtl->magic_scissors_started = TRUE;
2816 if ( track->trackpoints )
2817 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
2821 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2823 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2824 /* Also warn if overwrite old elevation data */
2825 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2827 vik_track_apply_dem_data ( track );
2830 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2832 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2835 trps = g_list_last(trps);
2836 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2839 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
2841 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2844 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2847 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
2849 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2852 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2855 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
2857 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2860 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2864 * Automatically change the viewport to center on the track and zoom to see the extent of the track
2866 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2868 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2869 if ( trps && *trps )
2871 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2872 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2873 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
2874 if ( pass_along[1] )
2875 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2877 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
2881 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
2883 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2884 trw_layer_tpwin_init ( vtl );
2887 /*************************************
2888 * merge/split by time routines
2889 *************************************/
2891 /* called for each key in track hash table.
2892 * If the current track has time stamp, add it to the result,
2893 * except the one pointed by "exclude".
2894 * set exclude to NULL if there is no exclude to check.
2895 * Not that result is in reverse (for performance reason).
2901 static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2903 twt_udata *user_data = udata;
2904 VikTrackpoint *p1, *p2;
2906 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2910 if (VIK_TRACK(value)->trackpoints) {
2911 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2912 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2914 if (!p1->has_timestamp || !p2->has_timestamp) {
2915 g_print("no timestamp\n");
2921 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2924 /* called for each key in track hash table. if original track user_data[1] is close enough
2925 * to the passed one, add it to list in user_data[0]
2927 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2930 VikTrackpoint *p1, *p2;
2932 GList **nearby_tracks = ((gpointer *)user_data)[0];
2933 GList *orig_track = ((gpointer *)user_data)[1];
2934 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
2937 * detect reasons for not merging, and return
2938 * if no reason is found not to merge, then do it.
2941 if (VIK_TRACK(value)->trackpoints == orig_track) {
2945 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2946 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2948 if (VIK_TRACK(value)->trackpoints) {
2949 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2950 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2952 if (!p1->has_timestamp || !p2->has_timestamp) {
2953 g_print("no timestamp\n");
2957 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2958 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2960 abs(p1->timestamp - t2) < thr*60)
2967 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2970 /* comparison function used to sort tracks; a and b are hash table keys */
2971 /* Not actively used - can be restored if needed
2972 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2974 GHashTable *tracks = user_data;
2977 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2978 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2980 if (t1 < t2) return -1;
2981 if (t1 > t2) return 1;
2986 /* comparison function used to sort trackpoints */
2987 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2989 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2991 if (t1 < t2) return -1;
2992 if (t1 > t2) return 1;
2996 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2998 * comparison function which can be used to sort tracks or waypoints by name
3000 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3002 const gchar* namea = (const gchar*) a;
3003 const gchar* nameb = (const gchar*) b;
3004 if ( namea == NULL || nameb == NULL)
3007 // Same sort method as used in the vik_treeview_*_alphabetize functions
3008 return strcmp ( namea, nameb );
3012 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3014 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3015 gchar *orig_track_name = pass_along[3];
3016 GList *tracks_with_timestamp = NULL;
3017 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3019 if (track->trackpoints &&
3020 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3021 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3028 udata.result = &tracks_with_timestamp;
3029 udata.exclude = track->trackpoints;
3030 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
3031 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3034 if (!tracks_with_timestamp) {
3035 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3039 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3040 // Sort alphabetically for user presentation
3041 tracks_with_timestamp = g_list_sort_with_data (tracks_with_timestamp, sort_alphabetically, NULL);
3044 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3045 tracks_with_timestamp, TRUE,
3046 _("Merge with..."), _("Select track to merge with"));
3047 g_list_free(tracks_with_timestamp);
3052 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3053 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3055 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3056 merge_track->trackpoints = NULL;
3057 vik_trw_layer_delete_track(vtl, l->data);
3058 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3061 /* TODO: free data before free merge_list */
3062 for (l = merge_list; l != NULL; l = g_list_next(l))
3064 g_list_free(merge_list);
3065 vik_layer_emit_update( VIK_LAYER(vtl) );
3069 /* merge by time routine */
3070 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3072 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3073 gchar *orig_track_name = strdup(pass_along[3]);
3076 GList *nearby_tracks;
3079 static guint thr = 1;
3080 guint track_count = 0;
3082 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3083 _("Merge Threshold..."),
3084 _("Merge when time between tracks less than:"),
3086 free(orig_track_name);
3090 /* merge tracks until we can't */
3091 nearby_tracks = NULL;
3095 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3096 trps = track->trackpoints;
3101 if (nearby_tracks) {
3102 g_list_free(nearby_tracks);
3103 nearby_tracks = NULL;
3106 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3107 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3109 /* g_print("Original track times: %d and %d\n", t1, t2); */
3110 params[0] = &nearby_tracks;
3112 params[2] = GUINT_TO_POINTER (thr);
3114 /* get a list of adjacent-in-time tracks */
3115 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3117 /* add original track */
3118 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3122 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3123 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3124 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3125 GList *l = nearby_tracks;
3126 VikTrack *tr = vik_track_new();
3127 tr->visible = track->visible;
3132 t1 = get_first_trackpoint(l)->timestamp;
3133 t2 = get_last_trackpoint(l)->timestamp;
3134 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3138 /* remove trackpoints from merged track, delete track */
3139 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3140 get_track(l)->trackpoints = NULL;
3141 vik_trw_layer_delete_track(vtl, l->data);
3146 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3147 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3149 #undef get_first_trackpoint
3150 #undef get_last_trackpoint
3153 } while (track_count > 1);
3154 g_list_free(nearby_tracks);
3155 free(orig_track_name);
3156 vik_layer_emit_update( VIK_LAYER(vtl) );
3159 /* split by time routine */
3160 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3162 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3163 GList *trps = track->trackpoints;
3165 GList *newlists = NULL;
3166 GList *newtps = NULL;
3168 static guint thr = 1;
3175 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3176 _("Split Threshold..."),
3177 _("Split when time between trackpoints exceeds:"),
3182 /* iterate through trackpoints, and copy them into new lists without touching original list */
3183 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3187 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3189 g_print("panic: ts < prev_ts: this should never happen!\n");
3192 if (ts - prev_ts > thr*60) {
3193 /* flush accumulated trackpoints into new list */
3194 newlists = g_list_append(newlists, g_list_reverse(newtps));
3198 /* accumulate trackpoint copies in newtps, in reverse order */
3199 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3201 iter = g_list_next(iter);
3204 newlists = g_list_append(newlists, g_list_reverse(newtps));
3207 /* put lists of trackpoints into tracks */
3210 // Only bother updating if the split results in new tracks
3211 if (g_list_length (newlists) > 1) {
3216 tr = vik_track_new();
3217 tr->visible = track->visible;
3218 tr->trackpoints = (GList *)(iter->data);
3220 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3221 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3222 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3223 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3225 iter = g_list_next(iter);
3227 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3228 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3230 g_list_free(newlists);
3234 * Split a track by the number of points as specified by the user
3236 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3238 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3239 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3241 // Check valid track
3242 GList *trps = track->trackpoints;
3246 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3247 _("Split Every Nth Point"),
3248 _("Split on every Nth point:"),
3249 250, // Default value as per typical limited track capacity of various GPS devices
3253 // Was a valid number returned?
3259 GList *newlists = NULL;
3260 GList *newtps = NULL;
3265 /* accumulate trackpoint copies in newtps, in reverse order */
3266 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3268 if (count >= points) {
3269 /* flush accumulated trackpoints into new list */
3270 newlists = g_list_append(newlists, g_list_reverse(newtps));
3274 iter = g_list_next(iter);
3277 // If there is a remaining chunk put that into the new split list
3278 // This may well be the whole track if no split points were encountered
3280 newlists = g_list_append(newlists, g_list_reverse(newtps));
3283 /* put lists of trackpoints into tracks */
3286 // Only bother updating if the split results in new tracks
3287 if (g_list_length (newlists) > 1) {
3292 tr = vik_track_new();
3293 tr->visible = track->visible;
3294 tr->trackpoints = (GList *)(iter->data);
3296 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3297 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3299 iter = g_list_next(iter);
3301 // Remove original track and then update the display
3302 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3303 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3305 g_list_free(newlists);
3308 /* end of split/merge routines */
3311 * Similar to trw_layer_enum_item, but this uses a sorted method
3313 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3315 GList **list = (GList**)udata;
3316 //*list = g_list_prepend(*all, key); //unsorted method
3317 // Sort named list alphabetically
3318 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3324 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3326 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3328 // Sort list alphabetically for better presentation
3329 g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3332 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3336 // Get list of items to delete from the user
3337 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3340 _("Delete Selection"),
3341 _("Select tracks to delete"));
3344 // Delete requested tracks
3345 // since specificly requested, IMHO no need for extra confirmation
3346 if ( delete_list ) {
3348 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3349 vik_trw_layer_delete_track(vtl, l->data);
3351 g_list_free(delete_list);
3352 vik_layer_emit_update( VIK_LAYER(vtl) );
3359 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3361 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3364 // Sort list alphabetically for better presentation
3365 g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3367 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3371 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3373 // Get list of items to delete from the user
3374 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3377 _("Delete Selection"),
3378 _("Select waypoints to delete"));
3381 // Delete requested waypoints
3382 // since specificly requested, IMHO no need for extra confirmation
3383 if ( delete_list ) {
3385 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3386 vik_trw_layer_delete_waypoint(vtl, l->data);
3388 g_list_free(delete_list);
3389 vik_layer_emit_update( VIK_LAYER(vtl) );
3394 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3396 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3398 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3401 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3403 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3404 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3408 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3410 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3415 if (strcmp(newname, sublayer) == 0 )
3418 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3419 if (g_hash_table_lookup( l->waypoints, newname))
3421 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3426 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3427 g_hash_table_steal ( l->waypoints_iters, sublayer );
3429 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3430 highest_wp_number_remove_wp(l, sublayer);
3431 g_hash_table_remove ( l->waypoints, sublayer );
3433 rv = g_strdup(newname);
3435 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3437 highest_wp_number_add_wp(l, rv);
3438 g_hash_table_insert ( l->waypoints, rv, wp );
3439 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3441 /* it hasn't been updated yet so we pass new name */
3442 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3443 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3446 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3449 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3456 if (strcmp(newname, sublayer) == 0)
3459 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3460 if (g_hash_table_lookup( l->tracks, newname))
3462 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3467 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3468 g_hash_table_steal ( l->tracks, sublayer );
3470 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3471 g_hash_table_steal ( l->tracks_iters, sublayer );
3473 rv = g_strdup(newname);
3475 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3477 g_hash_table_insert ( l->tracks, rv, tr );
3478 g_hash_table_insert ( l->tracks_iters, rv, iter );
3480 /* don't forget about current_tp_track_name, update that too */
3481 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3483 l->current_tp_track_name = rv;
3485 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3487 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3488 l->last_tp_track_name = rv;
3490 g_free ( orig_key );
3492 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3493 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3496 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3502 static gboolean is_valid_geocache_name ( gchar *str )
3504 gint len = strlen ( str );
3505 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]));
3508 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3510 gchar *track_name = (gchar *) pass_along[3];
3511 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3512 a_acquire_set_filter_track ( tr, track_name );
3515 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3517 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3518 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3521 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3523 gchar *track_name = (gchar *) pass_along[3];
3524 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3526 gchar *escaped = uri_escape ( tr->comment );
3527 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3528 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3534 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3535 /* viewpoint is now available instead */
3536 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3538 static gpointer pass_along[6];
3540 gboolean rv = FALSE;
3543 pass_along[1] = vlp;
3544 pass_along[2] = GINT_TO_POINTER (subtype);
3545 pass_along[3] = sublayer;
3546 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
3547 pass_along[5] = vvp;
3549 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3553 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3554 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3555 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3556 gtk_widget_show ( item );
3558 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3559 VikTrwLayer *vtl = l;
3560 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3561 if (tr && tr->property_dialog)
3562 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3565 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3566 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3567 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3568 gtk_widget_show ( item );
3570 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3572 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3573 gtk_widget_show ( item );
3575 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3576 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3577 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3578 gtk_widget_show ( item );
3580 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3582 gboolean separator_created = FALSE;
3584 /* could be a right-click using the tool */
3585 if ( vlp != NULL ) {
3586 item = gtk_menu_item_new ();
3587 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3588 gtk_widget_show ( item );
3590 separator_created = TRUE;
3592 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3593 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3594 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3595 gtk_widget_show ( item );
3598 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3600 if ( !separator_created ) {
3601 item = gtk_menu_item_new ();
3602 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3603 gtk_widget_show ( item );
3604 separator_created = TRUE;
3607 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3608 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3609 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3610 gtk_widget_show ( item );
3613 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
3615 if ( wp && wp->image )
3617 if ( !separator_created ) {
3618 item = gtk_menu_item_new ();
3619 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3620 gtk_widget_show ( item );
3621 separator_created = TRUE;
3624 // Set up image paramater
3625 pass_along[5] = wp->image;
3627 item = gtk_menu_item_new_with_mnemonic ( _("_Show Picture...") );
3628 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
3629 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3630 gtk_widget_show ( item );
3636 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3639 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3640 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3641 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3642 gtk_widget_show ( item );
3645 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
3647 item = gtk_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
3648 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3649 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3650 gtk_widget_show ( item );
3652 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3653 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3654 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3655 gtk_widget_show ( item );
3657 item = gtk_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
3658 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3659 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3660 gtk_widget_show ( item );
3662 item = gtk_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
3663 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3664 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3665 gtk_widget_show ( item );
3668 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
3672 item = gtk_menu_item_new_with_mnemonic ( _("_View All Tracks") );
3673 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3674 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3675 gtk_widget_show ( item );
3677 item = gtk_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
3678 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3679 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3680 gtk_widget_show ( item );
3682 item = gtk_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
3683 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3684 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3685 gtk_widget_show ( item );
3688 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3690 GtkWidget *goto_submenu;
3691 item = gtk_menu_item_new ();
3692 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3693 gtk_widget_show ( item );
3695 goto_submenu = gtk_menu_new ();
3696 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3697 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3698 gtk_widget_show ( item );
3699 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3701 item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
3702 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3703 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3704 gtk_widget_show ( item );
3706 item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3707 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3708 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3709 gtk_widget_show ( item );
3711 item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
3712 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3713 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3714 gtk_widget_show ( item );
3716 item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3717 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3718 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3719 gtk_widget_show ( item );
3721 item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3722 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3723 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3724 gtk_widget_show ( item );
3726 item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3727 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3728 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3729 gtk_widget_show ( item );
3731 item = gtk_menu_item_new_with_mnemonic ( _("_View Track") );
3732 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3733 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3734 gtk_widget_show ( item );
3736 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
3737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3738 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3739 gtk_widget_show ( item );
3741 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3742 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3743 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3744 gtk_widget_show ( item );
3746 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
3747 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3748 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3749 gtk_widget_show ( item );
3751 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
3752 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
3753 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3754 gtk_widget_show ( item );
3756 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
3758 item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3759 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3760 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3761 gtk_widget_show ( item );
3764 item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3765 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3766 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3767 gtk_widget_show ( item );
3769 item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
3770 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3771 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3772 gtk_widget_show ( item );
3774 item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3776 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3777 gtk_widget_show ( item );
3779 item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
3780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
3781 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3782 gtk_widget_show ( item );
3784 #ifdef VIK_CONFIG_OPENSTREETMAP
3785 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3786 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3787 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3788 gtk_widget_show ( item );
3791 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3793 item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3794 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3795 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3796 gtk_widget_show ( item );
3799 item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
3800 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3801 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3802 gtk_widget_show ( item );
3804 /* ATM This function is only available via the layers panel, due to needing a vlp */
3806 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3807 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3808 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3810 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3811 gtk_widget_show ( item );
3815 // Only show on viewport popmenu when a trackpoint is selected
3816 if ( ! vlp && l->current_tpl ) {
3818 item = gtk_menu_item_new ();
3819 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3820 gtk_widget_show ( item );
3822 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
3823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
3824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
3825 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3826 gtk_widget_show ( item );
3834 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3837 if (!vtl->current_tpl)
3839 if (!vtl->current_tpl->next)
3842 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3843 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3845 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3848 VikTrackpoint *tp_new = vik_trackpoint_new();
3849 struct LatLon ll_current, ll_next;
3850 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3851 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3853 /* main positional interpolation */
3854 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3855 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3857 /* Now other properties that can be interpolated */
3858 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3860 if (tp_current->has_timestamp && tp_next->has_timestamp) {
3861 /* Note here the division is applied to each part, then added
3862 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3863 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3864 tp_new->has_timestamp = TRUE;
3867 if (tp_current->speed != NAN && tp_next->speed != NAN)
3868 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3870 /* TODO - improve interpolation of course, as it may not be correct.
3871 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3872 [similar applies if value is in radians] */
3873 if (tp_current->course != NAN && tp_next->course != NAN)
3874 tp_new->speed = (tp_current->course + tp_next->course)/2;
3876 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3878 /* Insert new point into the trackpoints list after the current TP */
3879 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3880 gint index = g_list_index ( tr->trackpoints, tp_current );
3882 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3887 /* to be called when last_tpl no long exists. */
3888 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3890 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3891 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3892 vtl->last_tpl = NULL;
3893 vtl->last_tp_track_name = NULL;
3896 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3902 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3906 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3908 if ( vtl->current_tpl )
3910 vtl->current_tpl = NULL;
3911 vtl->current_tp_track_name = NULL;
3912 vik_layer_emit_update(VIK_LAYER(vtl));
3916 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3918 g_assert ( vtl->tpwin != NULL );
3919 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3920 trw_layer_cancel_current_tp ( vtl, TRUE );
3922 if ( vtl->current_tpl == NULL )
3925 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3927 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
3928 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
3930 VikTrack *tr = vik_track_new ();
3931 GList *newglist = g_list_alloc ();
3932 newglist->prev = NULL;
3933 newglist->next = vtl->current_tpl->next;
3934 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3935 tr->trackpoints = newglist;
3937 vtl->current_tpl->next->prev = newglist; /* end old track here */
3938 vtl->current_tpl->next = NULL;
3940 vtl->current_tpl = newglist; /* change tp to first of new track. */
3941 vtl->current_tp_track_name = name;
3943 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3947 vik_trw_layer_add_track ( vtl, name, tr );
3948 vik_layer_emit_update(VIK_LAYER(vtl));
3951 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3953 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3955 g_assert(tr != NULL);
3957 /* can't join with a non-existent trackpoint */
3958 vtl->last_tpl = NULL;
3959 vtl->last_tp_track_name = NULL;
3961 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3963 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3964 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3966 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3968 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3969 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3971 trw_layer_cancel_last_tp ( vtl );
3973 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3974 g_list_free_1 ( vtl->current_tpl );
3975 vtl->current_tpl = new_tpl;
3976 vik_layer_emit_update(VIK_LAYER(vtl));
3980 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3981 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3982 g_list_free_1 ( vtl->current_tpl );
3983 trw_layer_cancel_current_tp ( vtl, FALSE );
3986 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3988 vtl->last_tpl = vtl->current_tpl;
3989 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3990 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3992 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3994 vtl->last_tpl = vtl->current_tpl;
3995 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3996 vik_layer_emit_update(VIK_LAYER(vtl));
3998 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4000 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4001 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4003 VikTrack *tr_first = tr1, *tr_last = tr2;
4007 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4008 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4009 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4010 vik_track_reverse ( tr1 );
4011 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4016 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4018 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. */
4019 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4020 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4021 tr2->trackpoints = NULL;
4023 tmp = vtl->current_tp_track_name;
4025 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4026 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4028 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4029 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4030 vik_trw_layer_delete_track ( vtl, tmp );
4032 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4033 vik_layer_emit_update(VIK_LAYER(vtl));
4035 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4037 trw_layer_insert_tp_after_current_tp ( vtl );
4038 vik_layer_emit_update(VIK_LAYER(vtl));
4040 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4041 vik_layer_emit_update (VIK_LAYER(vtl));
4044 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4048 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4049 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4050 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4051 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4052 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4054 if ( vtl->current_tpl )
4055 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4056 /* set layer name and TP data */
4059 /***************************************************************************
4061 ***************************************************************************/
4063 /*** Utility data structures and functions ****/
4067 gint closest_x, closest_y;
4068 gchar *closest_wp_name;
4069 VikWaypoint *closest_wp;
4075 gint closest_x, closest_y;
4076 gchar *closest_track_name;
4077 VikTrackpoint *closest_tp;
4082 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4088 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4090 // If waypoint has an image then use the image size to select
4092 gint slackx, slacky;
4093 slackx = wp->image_width / 2;
4094 slacky = wp->image_height / 2;
4096 if ( x <= params->x + slackx && x >= params->x - slackx
4097 && y <= params->y + slacky && y >= params->y - slacky ) {
4098 params->closest_wp_name = name;
4099 params->closest_wp = wp;
4100 params->closest_x = x;
4101 params->closest_y = y;
4104 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4105 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
4106 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4108 params->closest_wp_name = name;
4109 params->closest_wp = wp;
4110 params->closest_x = x;
4111 params->closest_y = y;
4115 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4117 GList *tpl = t->trackpoints;
4126 tp = VIK_TRACKPOINT(tpl->data);
4128 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4130 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4131 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
4132 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4134 params->closest_track_name = name;
4135 params->closest_tp = tp;
4136 params->closest_tpl = tpl;
4137 params->closest_x = x;
4138 params->closest_y = y;
4144 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4146 TPSearchParams params;
4150 params.closest_track_name = NULL;
4151 params.closest_tp = NULL;
4152 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4153 return params.closest_tp;
4156 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4158 WPSearchParams params;
4162 params.closest_wp = NULL;
4163 params.closest_wp_name = NULL;
4164 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4165 return params.closest_wp;
4168 // Some forward declarations
4169 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4170 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4171 static void marker_end_move ( tool_ed_t *t );
4174 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4178 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4180 // Here always allow snapping back to the original location
4181 // this is useful when one decides not to move the thing afterall
4182 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4185 if ( event->state & GDK_CONTROL_MASK )
4187 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4189 new_coord = tp->coord;
4193 if ( event->state & GDK_SHIFT_MASK )
4195 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4197 new_coord = wp->coord;
4201 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4203 marker_moveto ( t, x, y );
4210 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4212 if ( t->holding && event->button == 1 )
4215 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4218 if ( event->state & GDK_CONTROL_MASK )
4220 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4222 new_coord = tp->coord;
4226 if ( event->state & GDK_SHIFT_MASK )
4228 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4230 new_coord = wp->coord;
4233 marker_end_move ( t );
4235 // Determine if working on a waypoint or a trackpoint
4236 if ( t->is_waypoint )
4237 vtl->current_wp->coord = new_coord;
4239 if ( vtl->current_tpl ) {
4240 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4243 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4245 // Don't really know what this is for but seems like it might be handy...
4246 /* can't join with itself! */
4247 trw_layer_cancel_last_tp ( vtl );
4252 vtl->current_wp = NULL;
4253 vtl->current_wp_name = NULL;
4254 trw_layer_cancel_current_tp ( vtl, FALSE );
4256 vik_layer_emit_update ( VIK_LAYER(vtl) );
4263 Returns true if a waypoint or track is found near the requested event position for this particular layer
4264 The item found is automatically selected
4265 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4267 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4269 if ( event->button != 1 )
4272 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4275 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4278 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4280 if (vtl->waypoints_visible) {
4281 WPSearchParams wp_params;
4282 wp_params.vvp = vvp;
4283 wp_params.x = event->x;
4284 wp_params.y = event->y;
4285 wp_params.closest_wp_name = NULL;
4286 wp_params.closest_wp = NULL;
4288 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4290 if ( wp_params.closest_wp ) {
4293 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4295 // Too easy to move it so must be holding shift to start immediately moving it
4296 // or otherwise be previously selected
4297 if ( event->state & GDK_SHIFT_MASK ||
4298 vtl->current_wp == wp_params.closest_wp ) {
4299 // Put into 'move buffer'
4300 // NB vvp & vw already set in tet
4301 tet->vtl = (gpointer)vtl;
4302 tet->is_waypoint = TRUE;
4304 marker_begin_move (tet, event->x, event->y);
4307 vtl->current_wp = wp_params.closest_wp;
4308 vtl->current_wp_name = wp_params.closest_wp_name;
4310 vik_layer_emit_update ( VIK_LAYER(vtl) );
4316 if (vtl->tracks_visible) {
4317 TPSearchParams tp_params;
4318 tp_params.vvp = vvp;
4319 tp_params.x = event->x;
4320 tp_params.y = event->y;
4321 tp_params.closest_track_name = NULL;
4322 tp_params.closest_tp = NULL;
4324 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4326 if ( tp_params.closest_tp ) {
4328 // Always select + highlight the track
4329 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4331 tet->is_waypoint = FALSE;
4333 // Select the Trackpoint
4334 // Can move it immediately when control held or it's the previously selected tp
4335 if ( event->state & GDK_CONTROL_MASK ||
4336 vtl->current_tpl == tp_params.closest_tpl ) {
4337 // Put into 'move buffer'
4338 // NB vvp & vw already set in tet
4339 tet->vtl = (gpointer)vtl;
4340 marker_begin_move (tet, event->x, event->y);
4343 vtl->current_tpl = tp_params.closest_tpl;
4344 vtl->current_tp_track_name = tp_params.closest_track_name;
4347 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4349 vik_layer_emit_update ( VIK_LAYER(vtl) );
4354 /* these aren't the droids you're looking for */
4355 vtl->current_wp = NULL;
4356 vtl->current_wp_name = NULL;
4357 trw_layer_cancel_current_tp ( vtl, FALSE );
4362 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4364 if ( event->button != 3 )
4367 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4370 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4373 /* Post menu for the currently selected item */
4375 /* See if a track is selected */
4376 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4377 if ( track && track->visible ) {
4379 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4381 if ( vtl->track_right_click_menu )
4382 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4384 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4386 vik_trw_layer_sublayer_add_menu_items ( vtl,
4387 vtl->track_right_click_menu,
4389 VIK_TRW_LAYER_SUBLAYER_TRACK,
4390 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4391 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4394 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4400 /* See if a waypoint is selected */
4401 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4402 if ( waypoint && waypoint->visible ) {
4403 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4405 if ( vtl->wp_right_click_menu )
4406 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4408 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4409 vik_trw_layer_sublayer_add_menu_items ( vtl,
4410 vtl->wp_right_click_menu,
4412 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4413 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4414 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4416 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4425 /* background drawing hook, to be passed the viewport */
4426 static gboolean tool_sync_done = TRUE;
4428 static gboolean tool_sync(gpointer data)
4430 VikViewport *vvp = data;
4431 gdk_threads_enter();
4432 vik_viewport_sync(vvp);
4433 tool_sync_done = TRUE;
4434 gdk_threads_leave();
4438 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4441 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4442 gdk_gc_set_function ( t->gc, GDK_INVERT );
4443 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4444 vik_viewport_sync(t->vvp);
4449 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4451 VikViewport *vvp = t->vvp;
4452 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4453 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4457 if (tool_sync_done) {
4458 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4459 tool_sync_done = FALSE;
4463 static void marker_end_move ( tool_ed_t *t )
4465 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4466 g_object_unref ( t->gc );
4470 /*** Edit waypoint ****/
4472 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4474 tool_ed_t *t = g_new(tool_ed_t, 1);
4480 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4482 WPSearchParams params;
4483 tool_ed_t *t = data;
4484 VikViewport *vvp = t->vvp;
4486 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4493 if ( !vtl->vl.visible || !vtl->waypoints_visible )
4496 if ( vtl->current_wp && vtl->current_wp->visible )
4498 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
4500 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
4502 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
4503 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
4505 if ( event->button == 3 )
4506 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4508 marker_begin_move(t, event->x, event->y);
4515 params.x = event->x;
4516 params.y = event->y;
4517 params.closest_wp_name = NULL;
4518 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4519 params.closest_wp = NULL;
4520 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4521 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
4523 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
4524 marker_begin_move(t, event->x, event->y);
4525 g_critical("shouldn't be here");
4528 else if ( params.closest_wp )
4530 if ( event->button == 3 )
4531 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4533 vtl->waypoint_rightclick = FALSE;
4535 vtl->current_wp = params.closest_wp;
4536 vtl->current_wp_name = params.closest_wp_name;
4538 if ( params.closest_wp )
4539 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ), TRUE );
4541 /* could make it so don't update if old WP is off screen and new is null but oh well */
4542 vik_layer_emit_update ( VIK_LAYER(vtl) );
4546 vtl->current_wp = NULL;
4547 vtl->current_wp_name = NULL;
4548 vtl->waypoint_rightclick = FALSE;
4549 vik_layer_emit_update ( VIK_LAYER(vtl) );
4553 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4555 tool_ed_t *t = data;
4556 VikViewport *vvp = t->vvp;
4558 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4563 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4566 if ( event->state & GDK_CONTROL_MASK )
4568 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4570 new_coord = tp->coord;
4574 if ( event->state & GDK_SHIFT_MASK )
4576 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4577 if ( wp && wp != vtl->current_wp )
4578 new_coord = wp->coord;
4583 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4585 marker_moveto ( t, x, y );
4592 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4594 tool_ed_t *t = data;
4595 VikViewport *vvp = t->vvp;
4597 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4600 if ( t->holding && event->button == 1 )
4603 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4606 if ( event->state & GDK_CONTROL_MASK )
4608 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4610 new_coord = tp->coord;
4614 if ( event->state & GDK_SHIFT_MASK )
4616 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4617 if ( wp && wp != vtl->current_wp )
4618 new_coord = wp->coord;
4621 marker_end_move ( t );
4623 vtl->current_wp->coord = new_coord;
4624 vik_layer_emit_update ( VIK_LAYER(vtl) );
4627 /* PUT IN RIGHT PLACE!!! */
4628 if ( event->button == 3 && vtl->waypoint_rightclick )
4630 if ( vtl->wp_right_click_menu )
4631 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
4632 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4633 vik_trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_name, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ), vvp );
4634 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4635 vtl->waypoint_rightclick = FALSE;
4640 /**** Begin track ***/
4641 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4646 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4648 vtl->current_track = NULL;
4649 return tool_new_track_click ( vtl, event, vvp );
4652 /*** New track ****/
4654 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4663 } new_track_move_passalong_t;
4665 /* sync and undraw, but only when we have time */
4666 static gboolean ct_sync ( gpointer passalong )
4668 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4669 vik_viewport_sync ( p->vvp );
4670 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4671 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4672 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4673 p->vtl->ct_sync_done = TRUE;
4678 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
4680 /* if we haven't sync'ed yet, we don't have time to do more. */
4681 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
4682 GList *iter = vtl->current_track->trackpoints;
4683 new_track_move_passalong_t *passalong;
4686 while ( iter->next )
4688 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
4689 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
4690 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
4691 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
4693 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
4694 passalong->vtl = vtl;
4695 passalong->vvp = vvp;
4698 passalong->x2 = event->x;
4699 passalong->y2 = event->y;
4701 /* this will sync and undraw when we have time to */
4702 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
4703 vtl->ct_sync_done = FALSE;
4704 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
4706 return VIK_LAYER_TOOL_ACK;
4709 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
4711 if ( vtl->current_track && event->keyval == GDK_Escape ) {
4712 vtl->current_track = NULL;
4713 vik_layer_emit_update ( VIK_LAYER(vtl) );
4715 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
4717 if ( vtl->current_track->trackpoints )
4719 GList *last = g_list_last(vtl->current_track->trackpoints);
4720 g_free ( last->data );
4721 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4723 vik_layer_emit_update ( VIK_LAYER(vtl) );
4729 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4733 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4736 if ( event->button == 3 && vtl->current_track )
4739 if ( vtl->current_track->trackpoints )
4741 GList *last = g_list_last(vtl->current_track->trackpoints);
4742 g_free ( last->data );
4743 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4745 vik_layer_emit_update ( VIK_LAYER(vtl) );
4749 if ( event->type == GDK_2BUTTON_PRESS )
4751 /* subtract last (duplicate from double click) tp then end */
4752 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
4754 GList *last = g_list_last(vtl->current_track->trackpoints);
4755 g_free ( last->data );
4756 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4757 /* undo last, then end */
4758 vtl->current_track = NULL;
4760 vik_layer_emit_update ( VIK_LAYER(vtl) );
4764 if ( ! vtl->current_track )
4766 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
4767 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
4769 vtl->current_track = vik_track_new();
4770 vtl->current_track->visible = TRUE;
4771 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
4773 /* incase it was created by begin track */
4774 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
4779 tp = vik_trackpoint_new();
4780 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
4782 /* snap to other TP */
4783 if ( event->state & GDK_CONTROL_MASK )
4785 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4787 tp->coord = other_tp->coord;
4790 tp->newsegment = FALSE;
4791 tp->has_timestamp = FALSE;
4793 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
4795 vtl->ct_x1 = vtl->ct_x2;
4796 vtl->ct_y1 = vtl->ct_y2;
4797 vtl->ct_x2 = event->x;
4798 vtl->ct_y2 = event->y;
4800 vik_layer_emit_update ( VIK_LAYER(vtl) );
4804 /*** New waypoint ****/
4806 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4811 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4814 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4816 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
4817 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
4818 vik_layer_emit_update ( VIK_LAYER(vtl) );
4823 /*** Edit trackpoint ****/
4825 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
4827 tool_ed_t *t = g_new(tool_ed_t, 1);
4833 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4835 tool_ed_t *t = data;
4836 VikViewport *vvp = t->vvp;
4837 TPSearchParams params;
4838 /* OUTDATED DOCUMENTATION:
4839 find 5 pixel range on each side. then put these UTM, and a pointer
4840 to the winning track name (and maybe the winning track itself), and a
4841 pointer to the winning trackpoint, inside an array or struct. pass
4842 this along, do a foreach on the tracks which will do a foreach on the
4845 params.x = event->x;
4846 params.y = event->y;
4847 params.closest_track_name = NULL;
4848 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4849 params.closest_tp = NULL;
4851 if ( event->button != 1 )
4854 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4857 if ( !vtl->vl.visible || !vtl->tracks_visible )
4860 if ( vtl->current_tpl )
4862 /* 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.) */
4863 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
4864 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
4866 g_assert ( current_tr );
4868 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
4870 if ( current_tr->visible &&
4871 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
4872 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
4873 marker_begin_move ( t, event->x, event->y );
4877 vtl->last_tpl = vtl->current_tpl;
4878 vtl->last_tp_track_name = vtl->current_tp_track_name;
4881 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4883 if ( params.closest_tp )
4885 vtl->current_tpl = params.closest_tpl;
4886 vtl->current_tp_track_name = params.closest_track_name;
4887 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ), TRUE );
4888 trw_layer_tpwin_init ( vtl );
4889 vik_layer_emit_update ( VIK_LAYER(vtl) );
4893 /* these aren't the droids you're looking for */
4897 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4899 tool_ed_t *t = data;
4900 VikViewport *vvp = t->vvp;
4902 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4908 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4911 if ( event->state & GDK_CONTROL_MASK )
4913 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4914 if ( tp && tp != vtl->current_tpl->data )
4915 new_coord = tp->coord;
4917 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4920 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4921 marker_moveto ( t, x, y );
4929 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4931 tool_ed_t *t = data;
4932 VikViewport *vvp = t->vvp;
4934 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4936 if ( event->button != 1)
4941 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4944 if ( event->state & GDK_CONTROL_MASK )
4946 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4947 if ( tp && tp != vtl->current_tpl->data )
4948 new_coord = tp->coord;
4951 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4953 marker_end_move ( t );
4955 /* diff dist is diff from orig */
4956 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4957 /* can't join with itself! */
4958 trw_layer_cancel_last_tp ( vtl );
4960 vik_layer_emit_update ( VIK_LAYER(vtl) );
4967 /*** Magic Scissors ***/
4968 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
4973 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4976 if ( !vtl ) return FALSE;
4977 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
4978 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
4980 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
4982 vtl->magic_scissors_coord = *new_end;
4984 vik_layer_emit_update ( VIK_LAYER(vtl) );
4985 /* remove last ' to:...' */
4986 if ( vtl->magic_scissors_current_track->comment ) {
4987 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
4988 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
4989 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
4990 last_to - vtl->magic_scissors_current_track->comment - 1);
4991 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4996 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
4997 struct LatLon start, end;
4998 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
4999 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5002 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
5003 vik_coord_to_latlon ( &(tmp), &end );
5004 vtl->magic_scissors_coord = tmp; /* for continuations */
5006 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5007 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
5008 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
5010 vtl->magic_scissors_check_added_track = TRUE;
5011 vtl->magic_scissors_started = FALSE;
5014 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5015 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5016 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5017 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5018 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5019 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5022 /* see if anything was done -- a track was added or appended to */
5023 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
5026 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
5029 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5031 vtl->magic_scissors_current_track = tr;
5033 g_free ( vtl->magic_scissors_added_track_name );
5034 vtl->magic_scissors_added_track_name = NULL;
5035 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
5036 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
5037 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
5038 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
5040 vtl->magic_scissors_check_added_track = FALSE;
5041 vtl->magic_scissors_append = FALSE;
5043 vik_layer_emit_update ( VIK_LAYER(vtl) );
5045 vtl->magic_scissors_started = TRUE;
5046 vtl->magic_scissors_coord = tmp;
5047 vtl->magic_scissors_current_track = NULL;
5052 /*** Show picture ****/
5054 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5059 /* Params are: vvp, event, last match found or NULL */
5060 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
5062 if ( wp->image && wp->visible )
5064 gint x, y, slackx, slacky;
5065 GdkEventButton *event = (GdkEventButton *) params[1];
5067 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5068 slackx = wp->image_width / 2;
5069 slacky = wp->image_height / 2;
5070 if ( x <= event->x + slackx && x >= event->x - slackx
5071 && y <= event->y + slacky && y >= event->y - slacky )
5073 params[2] = wp->image; /* we've found a match. however continue searching
5074 * since we want to find the last match -- that
5075 * is, the match that was drawn last. */
5080 static void trw_layer_show_picture ( gpointer pass_along[6] )
5082 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5084 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5087 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5088 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
5089 g_free ( quoted_file );
5090 if ( ! g_spawn_command_line_async ( cmd, &err ) )
5092 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch eog to open file.") );
5093 g_error_free ( err );
5096 #endif /* WINDOWS */
5099 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5101 gpointer params[3] = { vvp, event, NULL };
5102 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5104 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5107 static gpointer pass_along[6];
5108 pass_along[0] = vtl;
5109 pass_along[5] = params[2];
5110 trw_layer_show_picture ( pass_along );
5111 return TRUE; /* found a match */
5114 return FALSE; /* go through other layers, searching for a match */
5117 /***************************************************************************
5119 ***************************************************************************/
5125 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5127 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5128 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5131 /* Structure for thumbnail creating data used in the background thread */
5133 VikTrwLayer *vtl; // Layer needed for redrawing
5134 GSList *pics; // Image list
5135 } thumbnail_create_thread_data;
5137 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5139 guint total = g_slist_length(tctd->pics), done = 0;
5140 while ( tctd->pics )
5142 a_thumbnails_create ( (gchar *) tctd->pics->data );
5143 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5145 return -1; /* Abort thread */
5147 tctd->pics = tctd->pics->next;
5150 // Redraw to show the thumbnails as they are now created
5151 gdk_threads_enter();
5152 if ( IS_VIK_LAYER(tctd->vtl) )
5153 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) );
5154 gdk_threads_leave();
5159 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5161 while ( tctd->pics )
5163 g_free ( tctd->pics->data );
5164 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5169 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5171 if ( ! vtl->has_verified_thumbnails )
5173 GSList *pics = NULL;
5174 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5177 gint len = g_slist_length ( pics );
5178 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5179 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5182 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5184 (vik_thr_func) create_thumbnails_thread,
5186 (vik_thr_free_func) thumbnail_create_thread_free,
5194 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5196 return vtl->coord_mode;
5201 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5203 vik_coord_convert ( &(wp->coord), *dest_mode );
5206 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5208 vik_track_convert ( tr, *dest_mode );
5211 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5213 if ( vtl->coord_mode != dest_mode )
5215 vtl->coord_mode = dest_mode;
5216 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5217 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5221 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5223 return g_hash_table_lookup ( vtl->waypoints, name );
5226 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5228 return g_hash_table_lookup ( vtl->tracks, name );
5231 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
5233 vtl->menu_selection = selection;
5236 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
5238 return(vtl->menu_selection);
5241 /* ----------- Downloading maps along tracks --------------- */
5243 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5245 /* TODO: calculating based on current size of viewport */
5246 const gdouble w_at_zoom_0_125 = 0.0013;
5247 const gdouble h_at_zoom_0_125 = 0.0011;
5248 gdouble zoom_factor = zoom_level/0.125;
5250 wh->lat = h_at_zoom_0_125 * zoom_factor;
5251 wh->lon = w_at_zoom_0_125 * zoom_factor;
5253 return 0; /* all OK */
5256 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5258 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5259 (dist->lat >= ABS(to->north_south - from->north_south)))
5262 VikCoord *coord = g_malloc(sizeof(VikCoord));
5263 coord->mode = VIK_COORD_LATLON;
5265 if (ABS(gradient) < 1) {
5266 if (from->east_west > to->east_west)
5267 coord->east_west = from->east_west - dist->lon;
5269 coord->east_west = from->east_west + dist->lon;
5270 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5272 if (from->north_south > to->north_south)
5273 coord->north_south = from->north_south - dist->lat;
5275 coord->north_south = from->north_south + dist->lat;
5276 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5282 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5284 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5285 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5287 VikCoord *next = from;
5289 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5291 list = g_list_prepend(list, next);
5297 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5299 typedef struct _Rect {
5304 #define GLRECT(iter) ((Rect *)((iter)->data))
5307 GList *rects_to_download = NULL;
5310 if (get_download_area_width(vvp, zoom_level, &wh))
5313 GList *iter = tr->trackpoints;
5317 gboolean new_map = TRUE;
5318 VikCoord *cur_coord, tl, br;
5321 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5323 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5324 rect = g_malloc(sizeof(Rect));
5327 rect->center = *cur_coord;
5328 rects_to_download = g_list_prepend(rects_to_download, rect);
5333 gboolean found = FALSE;
5334 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5335 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
5346 GList *fillins = NULL;
5347 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
5348 /* seems that ATM the function get_next_coord works only for LATLON */
5349 if ( cur_coord->mode == VIK_COORD_LATLON ) {
5350 /* fill-ins for far apart points */
5351 GList *cur_rect, *next_rect;
5352 for (cur_rect = rects_to_download;
5353 (next_rect = cur_rect->next) != NULL;
5354 cur_rect = cur_rect->next) {
5355 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
5356 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
5357 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
5361 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
5364 GList *iter = fillins;
5366 cur_coord = (VikCoord *)(iter->data);
5367 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5368 rect = g_malloc(sizeof(Rect));
5371 rect->center = *cur_coord;
5372 rects_to_download = g_list_prepend(rects_to_download, rect);
5377 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5378 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
5382 for (iter = fillins; iter; iter = iter->next)
5384 g_list_free(fillins);
5386 if (rects_to_download) {
5387 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
5388 g_free(rect_iter->data);
5389 g_list_free(rects_to_download);
5393 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
5396 gint selected_map, default_map;
5397 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
5398 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
5399 gint selected_zoom, default_zoom;
5403 VikTrwLayer *vtl = pass_along[0];
5404 VikLayersPanel *vlp = pass_along[1];
5405 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5406 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
5408 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
5409 int num_maps = g_list_length(vmls);
5412 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
5416 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
5417 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
5419 gchar **np = map_names;
5420 VikMapsLayer **lp = map_layers;
5421 for (i = 0; i < num_maps; i++) {
5422 gboolean dup = FALSE;
5423 vml = (VikMapsLayer *)(vmls->data);
5424 for (j = 0; j < i; j++) { /* no duplicate allowed */
5425 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
5432 *np++ = vik_maps_layer_get_map_label(vml);
5438 num_maps = lp - map_layers;
5440 for (default_map = 0; default_map < num_maps; default_map++) {
5441 /* TODO: check for parent layer's visibility */
5442 if (VIK_LAYER(map_layers[default_map])->visible)
5445 default_map = (default_map == num_maps) ? 0 : default_map;
5447 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
5448 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
5449 if (cur_zoom == zoom_vals[default_zoom])
5452 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
5454 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
5457 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
5460 for (i = 0; i < num_maps; i++)
5461 g_free(map_names[i]);
5469 /**** lowest waypoint number calculation ***/
5470 static gint highest_wp_number_name_to_number(const gchar *name) {
5471 if ( strlen(name) == 3 ) {
5473 if ( n < 100 && name[0] != '0' )
5475 if ( n < 10 && name[0] != '0' )
5483 static void highest_wp_number_reset(VikTrwLayer *vtl)
5485 vtl->highest_wp_number = -1;
5488 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
5490 /* if is bigger that top, add it */
5491 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
5492 if ( new_wp_num > vtl->highest_wp_number )
5493 vtl->highest_wp_number = new_wp_num;
5496 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
5498 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
5499 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
5500 if ( vtl->highest_wp_number == old_wp_num ) {
5502 vtl->highest_wp_number --;
5504 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5505 /* search down until we find something that *does* exist */
5507 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
5508 vtl->highest_wp_number --;
5509 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5514 /* get lowest unused number */
5515 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
5518 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
5520 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
5521 return g_strdup(buf);