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_new_wikipedia_wp_viewport ( gpointer lav[2] );
254 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
257 static void trw_layer_properties_item ( gpointer pass_along[6] );
258 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
259 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
261 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
262 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
263 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
266 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
267 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
269 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
270 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
272 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
273 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
275 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
276 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
277 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
278 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
280 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
281 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
282 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
283 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
285 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
286 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
287 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
288 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
289 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
291 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
292 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
293 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
294 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
295 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
296 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
297 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
298 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
299 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
300 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
301 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
302 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
303 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
304 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
305 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
306 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
307 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
308 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
309 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
310 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
313 static void cached_pixbuf_free ( CachedPixbuf *cp );
314 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
315 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
317 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
318 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
320 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
322 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
323 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
324 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
326 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
327 static void highest_wp_number_reset(VikTrwLayer *vtl);
328 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
329 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
332 static VikToolInterface trw_layer_tools[] = {
333 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
334 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
336 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
337 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
338 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
340 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
341 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
343 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
344 (VikToolMouseFunc) tool_edit_waypoint_click,
345 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
346 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
348 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
349 (VikToolMouseFunc) tool_edit_trackpoint_click,
350 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
351 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
353 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
354 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
356 { N_("Magic Scissors"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
357 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
359 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
361 /****** PARAMETERS ******/
363 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
364 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
366 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
367 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
370 static VikLayerParamScale params_scales[] = {
371 /* min max step digits */
372 { 1, 10, 1, 0 }, /* line_thickness */
373 { 0.0, 99.0, 1, 2 }, /* velocity_min */
374 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
375 /* 5 * step == how much to turn */
376 { 16, 128, 3.2, 0 }, /* image_size */
377 { 0, 255, 5, 0 }, /* image alpha */
378 { 5, 500, 5, 0 }, /* image cache_size */
379 { 0, 8, 1, 0 }, /* image cache_size */
380 { 1, 64, 1, 0 }, /* wpsize */
381 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
382 { 1, 100, 1, 0 }, /* stop_length */
385 VikLayerParam trw_layer_params[] = {
386 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
387 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
389 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
390 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
391 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
392 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
393 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
395 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
396 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
398 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
399 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
400 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
401 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
402 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
404 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
405 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
406 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
407 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
408 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
409 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
410 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
411 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
413 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
414 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
415 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
416 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
419 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 };
422 *** 1) Add to trw_layer_params and enumeration
423 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
426 /****** END PARAMETERS ******/
428 VikLayerInterface vik_trw_layer_interface = {
433 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
437 params_groups, /* params_groups */
438 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
442 (VikLayerFuncCreate) vik_trw_layer_create,
443 (VikLayerFuncRealize) vik_trw_layer_realize,
444 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
445 (VikLayerFuncFree) vik_trw_layer_free,
447 (VikLayerFuncProperties) NULL,
448 (VikLayerFuncDraw) vik_trw_layer_draw,
449 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
451 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
452 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
454 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
455 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
457 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
458 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
459 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
460 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
461 (VikLayerFuncLayerSelected) vik_trw_layer_selected,
463 (VikLayerFuncMarshall) trw_layer_marshall,
464 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
466 (VikLayerFuncSetParam) trw_layer_set_param,
467 (VikLayerFuncGetParam) trw_layer_get_param,
469 (VikLayerFuncReadFileData) a_gpspoint_read_file,
470 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
472 (VikLayerFuncDeleteItem) trw_layer_del_item,
473 (VikLayerFuncCutItem) trw_layer_cut_item,
474 (VikLayerFuncCopyItem) trw_layer_copy_item,
475 (VikLayerFuncPasteItem) trw_layer_paste_item,
476 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
478 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
480 (VikLayerFuncSelectClick) trw_layer_select_click,
481 (VikLayerFuncSelectMove) trw_layer_select_move,
482 (VikLayerFuncSelectRelease) trw_layer_select_release,
483 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
486 /* for copy & paste (I think?) */
494 GType vik_trw_layer_get_type ()
496 static GType vtl_type = 0;
500 static const GTypeInfo vtl_info =
502 sizeof (VikTrwLayerClass),
503 NULL, /* base_init */
504 NULL, /* base_finalize */
505 NULL, /* class init */
506 NULL, /* class_finalize */
507 NULL, /* class_data */
508 sizeof (VikTrwLayer),
510 NULL /* instance init */
512 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
518 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
520 static gpointer pass_along[6];
526 pass_along[1] = NULL;
527 pass_along[2] = GINT_TO_POINTER (subtype);
528 pass_along[3] = sublayer;
529 pass_along[4] = NULL;
530 pass_along[5] = NULL;
532 trw_layer_delete_item ( pass_along );
535 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
537 static gpointer pass_along[6];
543 pass_along[1] = NULL;
544 pass_along[2] = GINT_TO_POINTER (subtype);
545 pass_along[3] = sublayer;
546 pass_along[4] = NULL;
547 pass_along[5] = NULL;
549 trw_layer_copy_item_cb(pass_along);
550 trw_layer_cut_item_cb(pass_along);
553 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
555 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
556 gint subtype = GPOINTER_TO_INT (pass_along[2]);
557 gpointer * sublayer = pass_along[3];
561 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
564 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
569 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
571 trw_layer_copy_item_cb(pass_along);
572 trw_layer_delete_item(pass_along);
575 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
586 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
588 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
590 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
593 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
594 fi = g_malloc ( *len );
595 fi->len = strlen(sublayer) + 1;
596 memcpy(fi->data, sublayer, fi->len);
597 memcpy(fi->data + fi->len, id, il);
599 *item = (guint8 *)fi;
602 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
604 FlatItem *fi = (FlatItem *) item;
606 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
611 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
612 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
613 vik_trw_layer_add_waypoint ( vtl, name, w );
614 waypoint_convert(name, w, &vtl->coord_mode);
615 // Consider if redraw necessary for the new item
616 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
617 vik_layer_emit_update ( VIK_LAYER(vtl) );
620 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
624 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
625 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
626 vik_trw_layer_add_track ( vtl, name, t );
627 track_convert(name, t, &vtl->coord_mode);
628 // Consider if redraw necessary for the new item
629 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
630 vik_layer_emit_update ( VIK_LAYER(vtl) );
636 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
643 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
647 case PARAM_TV: vtl->tracks_visible = data.b; break;
648 case PARAM_WV: vtl->waypoints_visible = data.b; break;
649 case PARAM_DM: vtl->drawmode = data.u; break;
650 case PARAM_DP: vtl->drawpoints = data.b; break;
651 case PARAM_DE: vtl->drawelevation = data.b; break;
652 case PARAM_DS: vtl->drawstops = data.b; break;
653 case PARAM_DL: vtl->drawlines = data.b; break;
654 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
655 vtl->stop_length = data.u;
657 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
658 vtl->elevation_factor = data.u;
660 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
662 vtl->line_thickness = data.u;
663 trw_layer_new_track_gcs ( vtl, vp );
666 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
668 vtl->bg_line_thickness = data.u;
669 trw_layer_new_track_gcs ( vtl, vp );
674 /* Convert to store internally
675 NB file operation always in internal units (metres per second) */
676 vik_units_speed_t speed_units = a_vik_get_units_speed ();
677 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
678 vtl->velocity_min = data.d;
679 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
680 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
681 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
682 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
685 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
690 /* Convert to store internally
691 NB file operation always in internal units (metres per second) */
692 vik_units_speed_t speed_units = a_vik_get_units_speed ();
693 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
694 vtl->velocity_max = data.d;
695 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
696 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
697 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
698 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
701 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
704 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
705 case PARAM_DLA: vtl->drawlabels = data.b; break;
706 case PARAM_DI: vtl->drawimages = data.b; break;
707 case PARAM_IS: if ( data.u != vtl->image_size )
709 vtl->image_size = data.u;
710 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
711 g_queue_free ( vtl->image_cache );
712 vtl->image_cache = g_queue_new ();
715 case PARAM_IA: vtl->image_alpha = data.u; break;
716 case PARAM_ICS: vtl->image_cache_size = data.u;
717 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
718 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
720 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
721 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
722 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
723 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
724 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
725 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
726 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
731 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
733 VikLayerParamData rv;
736 case PARAM_TV: rv.b = vtl->tracks_visible; break;
737 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
738 case PARAM_DM: rv.u = vtl->drawmode; break;
739 case PARAM_DP: rv.b = vtl->drawpoints; break;
740 case PARAM_DE: rv.b = vtl->drawelevation; break;
741 case PARAM_EF: rv.u = vtl->elevation_factor; break;
742 case PARAM_DS: rv.b = vtl->drawstops; break;
743 case PARAM_SL: rv.u = vtl->stop_length; break;
744 case PARAM_DL: rv.b = vtl->drawlines; break;
745 case PARAM_LT: rv.u = vtl->line_thickness; break;
746 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
749 /* Convert to store internally
750 NB file operation always in internal units (metres per second) */
751 vik_units_speed_t speed_units = a_vik_get_units_speed ();
752 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
753 rv.d = vtl->velocity_min;
754 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
755 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
756 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
757 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
760 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
765 /* Convert to store internally
766 NB file operation always in internal units (metres per second) */
767 vik_units_speed_t speed_units = a_vik_get_units_speed ();
768 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
769 rv.d = vtl->velocity_max;
770 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
771 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
772 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
773 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
776 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
779 case PARAM_DLA: rv.b = vtl->drawlabels; break;
780 case PARAM_DI: rv.b = vtl->drawimages; break;
781 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
782 case PARAM_IS: rv.u = vtl->image_size; break;
783 case PARAM_IA: rv.u = vtl->image_alpha; break;
784 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
785 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
786 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
787 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
788 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
789 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
790 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
791 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
796 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
807 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
808 a_gpx_write_file(vtl, f);
809 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
812 g_file_get_contents(tmpname, &dd, &dl, NULL);
813 *len = sizeof(pl) + pl + dl;
814 *data = g_malloc(*len);
815 memcpy(*data, &pl, sizeof(pl));
816 memcpy(*data + sizeof(pl), pd, pl);
817 memcpy(*data + sizeof(pl) + pl, dd, dl);
826 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
828 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
834 memcpy(&pl, data, sizeof(pl));
836 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
839 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
840 g_critical("couldn't open temp file");
843 fwrite(data, len - pl - sizeof(pl), 1, f);
845 a_gpx_read_file(rv, f);
853 static GList * str_array_to_glist(gchar* data[])
857 for (p = (gpointer)data; *p; p++)
858 gl = g_list_prepend(gl, *p);
859 return(g_list_reverse(gl));
862 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
864 return (strcasecmp(s1, s2) == 0);
867 static guint strcase_hash(gconstpointer v)
869 /* 31 bit hash function */
872 gchar s[128]; /* malloc is too slow for reading big files */
875 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
876 p[i] = toupper(t[i]);
882 for (p += 1; *p != '\0'; p++)
883 h = (h << 5) - h + *p;
889 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
891 if (trw_layer_params[PARAM_DM].widget_data == NULL)
892 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
893 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
894 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
896 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
897 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
899 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
900 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
901 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
902 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
904 /* TODO: constants at top */
905 rv->waypoints_visible = rv->tracks_visible = TRUE;
906 rv->drawmode = drawmode;
907 rv->drawpoints = TRUE;
908 rv->drawstops = FALSE;
909 rv->drawelevation = FALSE;
910 rv->elevation_factor = 30;
911 rv->stop_length = 60;
912 rv->drawlines = TRUE;
913 rv->wplabellayout = NULL;
914 rv->wp_right_click_menu = NULL;
915 rv->track_right_click_menu = NULL;
916 rv->waypoint_gc = NULL;
917 rv->waypoint_text_gc = NULL;
918 rv->waypoint_bg_gc = NULL;
920 rv->velocity_max = 5.0;
921 rv->velocity_min = 0.0;
922 rv->line_thickness = 1;
923 rv->bg_line_thickness = 0;
924 rv->current_wp = NULL;
925 rv->current_wp_name = NULL;
926 rv->current_track = NULL;
927 rv->current_tpl = NULL;
928 rv->current_tp_track_name = NULL;
929 rv->moving_tp = FALSE;
930 rv->moving_wp = FALSE;
932 rv->ct_sync_done = TRUE;
934 rv->magic_scissors_started = FALSE;
935 rv->magic_scissors_check_added_track = FALSE;
936 rv->magic_scissors_added_track_name = NULL;
937 rv->magic_scissors_current_track = NULL;
938 rv->magic_scissors_append = FALSE;
940 rv->waypoint_rightclick = FALSE;
942 rv->last_tp_track_name = NULL;
944 rv->image_cache = g_queue_new();
946 rv->image_alpha = 255;
947 rv->image_cache_size = 300;
948 rv->drawimages = TRUE;
949 rv->drawlabels = TRUE;
954 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
956 g_hash_table_destroy(trwlayer->waypoints);
957 g_hash_table_destroy(trwlayer->tracks);
959 /* ODC: replace with GArray */
960 trw_layer_free_track_gcs ( trwlayer );
962 if ( trwlayer->wp_right_click_menu )
963 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
965 if ( trwlayer->track_right_click_menu )
966 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
968 if ( trwlayer->wplabellayout != NULL)
969 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
971 if ( trwlayer->waypoint_gc != NULL )
972 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
974 if ( trwlayer->waypoint_text_gc != NULL )
975 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
977 if ( trwlayer->waypoint_bg_gc != NULL )
978 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
980 if ( trwlayer->waypoint_font != NULL )
981 gdk_font_unref ( trwlayer->waypoint_font );
983 if ( trwlayer->tpwin != NULL )
984 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
986 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
987 g_queue_free ( trwlayer->image_cache );
990 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
993 dp->xmpp = vik_viewport_get_xmpp ( vp );
994 dp->ympp = vik_viewport_get_ympp ( vp );
995 dp->width = vik_viewport_get_width ( vp );
996 dp->height = vik_viewport_get_height ( vp );
997 dp->center = vik_viewport_get_center ( vp );
998 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
999 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1004 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1005 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1006 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1008 dp->ce1 = dp->center->east_west-w2;
1009 dp->ce2 = dp->center->east_west+w2;
1010 dp->cn1 = dp->center->north_south-h2;
1011 dp->cn2 = dp->center->north_south+h2;
1012 } else if ( dp->lat_lon ) {
1013 VikCoord upperleft, bottomright;
1014 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1015 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1016 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1017 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1018 dp->ce1 = upperleft.east_west;
1019 dp->ce2 = bottomright.east_west;
1020 dp->cn1 = bottomright.north_south;
1021 dp->cn2 = upperleft.north_south;
1024 dp->track_gc_iter = 0;
1027 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1029 static gdouble rv = 0;
1030 if ( tp1->has_timestamp && tp2->has_timestamp )
1032 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1033 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1036 return VIK_TRW_LAYER_TRACK_GC_MIN;
1037 else if ( vtl->velocity_min >= vtl->velocity_max )
1038 return VIK_TRW_LAYER_TRACK_GC_MAX;
1040 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1042 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1043 return VIK_TRW_LAYER_TRACK_GC_MAX;
1047 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1050 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1052 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1053 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1054 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1055 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1058 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1060 /* TODO: this function is a mess, get rid of any redundancy */
1061 GList *list = track->trackpoints;
1063 gboolean useoldvals = TRUE;
1065 gboolean drawpoints;
1067 gboolean drawelevation;
1068 gdouble min_alt, max_alt, alt_diff = 0;
1070 const guint8 tp_size_reg = 2;
1071 const guint8 tp_size_cur = 4;
1074 if ( dp->vtl->drawelevation )
1076 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1077 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1078 alt_diff = max_alt - min_alt;
1081 if ( ! track->visible )
1084 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1085 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1086 trw_layer_draw_track ( name, track, dp, TRUE );
1088 if ( drawing_white_background )
1089 drawpoints = drawstops = FALSE;
1091 drawpoints = dp->vtl->drawpoints;
1092 drawstops = dp->vtl->drawstops;
1095 /* Current track - used for creation */
1096 if ( track == dp->vtl->current_track )
1097 main_gc = dp->vtl->current_track_gc;
1099 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1100 /* Draw all tracks of the layer in special colour */
1101 /* if track is member of selected layer or is the current selected track
1102 then draw in the highlight colour.
1103 NB this supercedes the drawmode */
1104 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1105 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1106 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1107 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1110 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1111 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1113 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1117 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1118 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1120 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1125 int x, y, oldx, oldy;
1126 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1128 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1130 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1132 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1134 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1135 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1141 while ((list = g_list_next(list)))
1143 tp = VIK_TRACKPOINT(list->data);
1144 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1146 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1147 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1148 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1149 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1150 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1152 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1154 if ( drawpoints && ! drawing_white_background )
1157 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1160 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1161 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 );
1164 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 );
1167 if ((!tp->newsegment) && (dp->vtl->drawlines))
1169 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1171 /* UTM only: zone check */
1172 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1173 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1175 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1176 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1177 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1181 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1183 if ( drawing_white_background ) {
1184 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1188 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1189 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1191 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1192 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1196 tmp[1].y = oldy-FIXALTITUDE(list->data);
1198 tmp[2].y = y-FIXALTITUDE(list->next->data);
1203 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1204 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1206 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1207 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1209 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1219 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1221 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1222 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1224 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1225 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1226 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1227 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1230 if ( drawing_white_background )
1231 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1233 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1237 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1238 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1245 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1246 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1247 dp->track_gc_iter = 0;
1250 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1251 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1253 trw_layer_draw_track ( name, track, dp, FALSE );
1256 static void cached_pixbuf_free ( CachedPixbuf *cp )
1258 g_object_unref ( G_OBJECT(cp->pixbuf) );
1259 g_free ( cp->image );
1262 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1264 return strcmp ( cp->image, name );
1267 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1270 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1271 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1272 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1275 GdkPixbuf *sym = NULL;
1276 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1278 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1280 if ( wp->image && dp->vtl->drawimages )
1282 GdkPixbuf *pixbuf = NULL;
1285 if ( dp->vtl->image_alpha == 0)
1288 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1290 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1293 gchar *image = wp->image;
1294 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1295 if ( ! regularthumb )
1297 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1298 image = "\x12\x00"; /* this shouldn't occur naturally. */
1302 CachedPixbuf *cp = NULL;
1303 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1304 if ( dp->vtl->image_size == 128 )
1305 cp->pixbuf = regularthumb;
1308 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1309 g_assert ( cp->pixbuf );
1310 g_object_unref ( G_OBJECT(regularthumb) );
1312 cp->image = g_strdup ( image );
1314 /* needed so 'click picture' tool knows how big the pic is; we don't
1315 * store it in cp because they may have been freed already. */
1316 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1317 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1319 g_queue_push_head ( dp->vtl->image_cache, cp );
1320 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1321 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1323 pixbuf = cp->pixbuf;
1327 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1333 w = gdk_pixbuf_get_width ( pixbuf );
1334 h = gdk_pixbuf_get_height ( pixbuf );
1336 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1338 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1339 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1340 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1341 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1342 // Highlighted - so draw a little border around the chosen one
1343 // single line seems a little weak so draw 2 of them
1344 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1345 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1346 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1347 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1350 if ( dp->vtl->image_alpha == 255 )
1351 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1353 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1355 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1359 /* DRAW ACTUAL DOT */
1360 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1361 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 );
1363 else if ( wp == dp->vtl->current_wp ) {
1364 switch ( dp->vtl->wp_symbol ) {
1365 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;
1366 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;
1367 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;
1368 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 );
1369 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 );
1373 switch ( dp->vtl->wp_symbol ) {
1374 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;
1375 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;
1376 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;
1377 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 );
1378 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;
1382 if ( dp->vtl->drawlabels )
1384 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1385 gint label_x, label_y;
1387 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1388 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1389 label_x = x - width/2;
1391 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1393 label_y = y - dp->vtl->wp_size - height - 2;
1395 /* if highlight mode on, then draw background text in highlight colour */
1396 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1397 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1398 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1399 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1400 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1402 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1405 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1407 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1412 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1414 static struct DrawingParams dp;
1415 g_assert ( l != NULL );
1417 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1420 if ( l->tracks_visible )
1421 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1423 if (l->waypoints_visible)
1424 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1427 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1430 if ( vtl->track_bg_gc )
1432 g_object_unref ( vtl->track_bg_gc );
1433 vtl->track_bg_gc = NULL;
1435 if ( vtl->current_track_gc )
1437 g_object_unref ( vtl->current_track_gc );
1438 vtl->current_track_gc = NULL;
1441 if ( ! vtl->track_gc )
1443 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1444 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1445 g_array_free ( vtl->track_gc, TRUE );
1446 vtl->track_gc = NULL;
1449 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1451 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1452 gint width = vtl->line_thickness;
1454 if ( vtl->track_gc )
1455 trw_layer_free_track_gcs ( vtl );
1457 if ( vtl->track_bg_gc )
1458 g_object_unref ( vtl->track_bg_gc );
1459 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1461 if ( vtl->current_track_gc )
1462 g_object_unref ( vtl->current_track_gc );
1463 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1464 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1466 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1468 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1470 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1471 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1472 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1473 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1474 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1475 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1476 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1477 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1478 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1479 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1481 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1483 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1485 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1488 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1490 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1491 PangoFontDescription *pfd;
1492 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1493 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1494 pango_layout_set_font_description (rv->wplabellayout, pfd);
1495 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1496 pango_font_description_free (pfd);
1498 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1500 trw_layer_new_track_gcs ( rv, vp );
1502 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1503 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1504 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1505 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1507 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1509 rv->has_verified_thumbnails = FALSE;
1510 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1512 rv->wp_draw_symbols = TRUE;
1514 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1516 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1521 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1523 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1525 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1526 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 );
1528 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 );
1531 *new_iter = *((GtkTreeIter *) pass_along[1]);
1532 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1534 if ( ! track->visible )
1535 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1538 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1540 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1541 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1542 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 );
1544 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 );
1547 *new_iter = *((GtkTreeIter *) pass_along[1]);
1548 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1550 if ( ! wp->visible )
1551 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1555 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1558 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1560 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1561 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1563 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1565 if ( ! vtl->tracks_visible )
1566 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1568 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1570 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1571 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1573 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1576 if ( ! vtl->waypoints_visible )
1577 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1579 pass_along[0] = &(vtl->waypoints_iter);
1580 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1582 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1586 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1590 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1591 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1592 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1594 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1596 return (t->visible ^= 1);
1600 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1602 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1604 return (t->visible ^= 1);
1612 // Structure to hold multiple track information for a layer
1621 * Build up layer multiple track information via updating the tooltip_tracks structure
1623 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1625 tt->length = tt->length + vik_track_get_length (tr);
1627 // Ensure times are available
1628 if ( tr->trackpoints &&
1629 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1630 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1633 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1634 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1636 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1637 // Hence initialize to the first 'proper' value
1638 if ( tt->start_time == 0 )
1639 tt->start_time = t1;
1640 if ( tt->end_time == 0 )
1643 // Update find the earliest / last times
1644 if ( t1 < tt->start_time )
1645 tt->start_time = t1;
1646 if ( t2 > tt->end_time )
1649 // Keep track of total time
1650 // there maybe gaps within a track (eg segments)
1651 // but this should be generally good enough for a simple indicator
1652 tt->duration = tt->duration + (int)(t2-t1);
1657 * Generate tooltip text for the layer.
1658 * This is relatively complicated as it considers information for
1659 * no tracks, a single track or multiple tracks
1660 * (which may or may not have timing information)
1662 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1673 static gchar tmp_buf[128];
1676 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1678 // Safety check - I think these should always be valid
1679 if ( vtl->tracks && vtl->waypoints ) {
1680 tooltip_tracks tt = { 0.0, 0, 0 };
1681 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1683 GDate* gdate_start = g_date_new ();
1684 g_date_set_time_t (gdate_start, tt.start_time);
1686 GDate* gdate_end = g_date_new ();
1687 g_date_set_time_t (gdate_end, tt.end_time);
1689 if ( g_date_compare (gdate_start, gdate_end) ) {
1690 // Dates differ so print range on separate line
1691 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1692 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1693 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1696 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1697 if ( tt.start_time != 0 )
1698 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1702 if ( tt.length > 0.0 ) {
1703 gdouble len_in_units;
1705 // Setup info dependent on distance units
1706 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1707 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1708 len_in_units = VIK_METERS_TO_MILES(tt.length);
1711 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1712 len_in_units = tt.length/1000.0;
1715 // Timing information if available
1717 if ( tt.duration > 0 ) {
1718 g_snprintf (tbuf1, sizeof(tbuf1),
1719 _(" in %d:%02d hrs:mins"),
1720 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1722 g_snprintf (tbuf2, sizeof(tbuf2),
1723 _("\n%sTotal Length %.1f %s%s"),
1724 tbuf3, len_in_units, tbuf4, tbuf1);
1727 // Put together all the elements to form compact tooltip text
1728 g_snprintf (tmp_buf, sizeof(tmp_buf),
1729 _("Tracks: %d - Waypoints: %d%s"),
1730 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1732 g_date_free (gdate_start);
1733 g_date_free (gdate_end);
1740 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1744 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1745 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1746 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1748 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1750 // Could be a better way of handling strings - but this works...
1751 gchar time_buf1[20];
1752 gchar time_buf2[20];
1753 time_buf1[0] = '\0';
1754 time_buf2[0] = '\0';
1755 static gchar tmp_buf[100];
1756 // Compact info: Short date eg (11/20/99), duration and length
1757 // Hopefully these are the things that are most useful and so promoted into the tooltip
1758 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1759 // %x The preferred date representation for the current locale without the time.
1760 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1761 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1762 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1764 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1767 // Get length and consider the appropriate distance units
1768 gdouble tr_len = vik_track_get_length(tr);
1769 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1770 switch (dist_units) {
1771 case VIK_UNITS_DISTANCE_KILOMETRES:
1772 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1774 case VIK_UNITS_DISTANCE_MILES:
1775 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1784 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1786 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1787 // NB It's OK to return NULL
1797 * General layer selection function, find out which bit is selected and take appropriate action
1799 gboolean vik_trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1802 l->current_wp = NULL;
1803 l->current_wp_name = NULL;
1804 trw_layer_cancel_current_tp ( l, FALSE );
1808 case VIK_TREEVIEW_TYPE_LAYER:
1810 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1811 /* Mark for redraw */
1816 case VIK_TREEVIEW_TYPE_SUBLAYER:
1820 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1822 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1823 /* Mark for redraw */
1827 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1829 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->tracks, sublayer ), l, sublayer );
1830 /* Mark for redraw */
1834 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1836 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
1837 /* Mark for redraw */
1841 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1843 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->waypoints, sublayer ), l, sublayer );
1844 /* Mark for redraw */
1850 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1859 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1864 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1869 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1871 return l->waypoints;
1874 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1876 static VikCoord fixme;
1877 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1878 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1879 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1880 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1881 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1882 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1883 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1884 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1885 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1888 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1891 static VikCoord fixme;
1895 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1896 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1897 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1898 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1899 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1900 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1901 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1902 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1903 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1908 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1910 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1911 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1913 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1914 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1915 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1916 maxmin[0].lat = wpt_maxmin[0].lat;
1919 maxmin[0].lat = trk_maxmin[0].lat;
1921 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1922 maxmin[0].lon = wpt_maxmin[0].lon;
1925 maxmin[0].lon = trk_maxmin[0].lon;
1927 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1928 maxmin[1].lat = wpt_maxmin[1].lat;
1931 maxmin[1].lat = trk_maxmin[1].lat;
1933 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1934 maxmin[1].lon = wpt_maxmin[1].lon;
1937 maxmin[1].lon = trk_maxmin[1].lon;
1941 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1943 /* 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... */
1944 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1945 trw_layer_find_maxmin (vtl, maxmin);
1946 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1950 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1951 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1956 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1959 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1960 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
1962 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1965 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1967 /* First set the center [in case previously viewing from elsewhere] */
1968 /* Then loop through zoom levels until provided positions are in view */
1969 /* This method is not particularly fast - but should work well enough */
1970 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1972 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1973 vik_viewport_set_center_coord ( vvp, &coord );
1975 /* Convert into definite 'smallest' and 'largest' positions */
1976 struct LatLon minmin;
1977 if ( maxmin[0].lat < maxmin[1].lat )
1978 minmin.lat = maxmin[0].lat;
1980 minmin.lat = maxmin[1].lat;
1982 struct LatLon maxmax;
1983 if ( maxmin[0].lon > maxmin[1].lon )
1984 maxmax.lon = maxmin[0].lon;
1986 maxmax.lon = maxmin[1].lon;
1988 /* Never zoom in too far - generally not that useful, as too close ! */
1989 /* Always recalculate the 'best' zoom level */
1991 vik_viewport_set_zoom ( vvp, zoom );
1993 gdouble min_lat, max_lat, min_lon, max_lon;
1994 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1995 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1996 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1997 /* NB I think the logic used in this test to determine if the bounds is within view
1998 fails if track goes across 180 degrees longitude.
1999 Hopefully that situation is not too common...
2000 Mind you viking doesn't really do edge locations to well anyway */
2001 if ( min_lat < minmin.lat &&
2002 max_lat > minmin.lat &&
2003 min_lon < maxmax.lon &&
2004 max_lon > maxmax.lon )
2005 /* Found within zoom level */
2010 vik_viewport_set_zoom ( vvp, zoom );
2014 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2016 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2017 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2018 trw_layer_find_maxmin (vtl, maxmin);
2019 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2022 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2027 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2029 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])) ) ) {
2030 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2033 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2036 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2038 GtkWidget *file_selector;
2040 gboolean failed = FALSE;
2041 file_selector = gtk_file_chooser_dialog_new (title,
2043 GTK_FILE_CHOOSER_ACTION_SAVE,
2044 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2045 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2047 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2049 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2051 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2052 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2054 gtk_widget_hide ( file_selector );
2055 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2060 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2062 gtk_widget_hide ( file_selector );
2063 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2068 gtk_widget_destroy ( file_selector );
2070 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2073 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2075 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2078 static void trw_layer_export_gpsmapper ( 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_GPSMAPPER );
2083 static void trw_layer_export_gpx ( 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_GPX );
2088 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2090 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2091 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2092 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2093 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2095 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2097 g_free ( auto_save_name );
2100 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2102 gpointer layer_and_vlp[2];
2103 layer_and_vlp[0] = pass_along[0];
2104 layer_and_vlp[1] = pass_along[1];
2106 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2107 gchar *auto_save_name = g_strdup ( pass_along[3] );
2108 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2109 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2111 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2113 g_free ( auto_save_name );
2116 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2118 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2119 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2120 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2121 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2123 GTK_RESPONSE_REJECT,
2125 GTK_RESPONSE_ACCEPT,
2128 GtkWidget *label, *entry;
2129 label = gtk_label_new(_("Waypoint Name:"));
2130 entry = gtk_entry_new();
2132 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2133 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2134 gtk_widget_show_all ( label );
2135 gtk_widget_show_all ( entry );
2137 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2139 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2142 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2145 for ( i = strlen(upname)-1; i >= 0; i-- )
2146 upname[i] = toupper(upname[i]);
2148 wp = g_hash_table_lookup ( wps, upname );
2151 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2154 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2155 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2156 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 );
2163 gtk_widget_destroy ( dia );
2166 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2168 gchar *default_name = highest_wp_number_get(vtl);
2169 VikWaypoint *wp = vik_waypoint_new();
2170 gchar *returned_name;
2172 wp->coord = *def_coord;
2174 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2177 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2178 g_free (default_name);
2181 g_free (default_name);
2182 vik_waypoint_free(wp);
2186 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2189 struct LatLon one_ll, two_ll;
2190 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2192 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2193 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2194 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2195 VikViewport *vvp = vik_window_viewport(vw);
2196 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2197 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2198 vik_coord_to_latlon(&one, &one_ll);
2199 vik_coord_to_latlon(&two, &two_ll);
2200 if (one_ll.lat > two_ll.lat) {
2201 maxmin[0].lat = one_ll.lat;
2202 maxmin[1].lat = two_ll.lat;
2205 maxmin[0].lat = two_ll.lat;
2206 maxmin[1].lat = one_ll.lat;
2208 if (one_ll.lon > two_ll.lon) {
2209 maxmin[0].lon = one_ll.lon;
2210 maxmin[1].lon = two_ll.lon;
2213 maxmin[0].lon = two_ll.lon;
2214 maxmin[1].lon = one_ll.lon;
2216 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2219 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2221 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2222 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2223 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2225 trw_layer_find_maxmin (vtl, maxmin);
2226 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2229 static void trw_layer_new_wp ( gpointer lav[2] )
2231 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2232 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2233 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2234 instead return true if you want to update. */
2235 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 )
2236 vik_layers_panel_emit_update ( vlp );
2239 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2241 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2242 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2244 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2245 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2246 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2247 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2248 vik_layers_panel_emit_update ( vlp );
2252 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2254 /* NB do not care if wp is visible or not */
2255 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2258 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2260 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2261 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2263 /* Only 1 waypoint - jump straight to it */
2264 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2265 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2266 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2268 /* If at least 2 waypoints - find center and then zoom to fit */
2269 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2271 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2272 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2273 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2276 vik_layers_panel_emit_update ( vlp );
2279 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2281 static gpointer pass_along[2];
2283 GtkWidget *export_submenu;
2284 GtkWidget *wikipedia_submenu;
2285 pass_along[0] = vtl;
2286 pass_along[1] = vlp;
2288 item = gtk_menu_item_new();
2289 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2290 gtk_widget_show ( item );
2292 item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") );
2293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2294 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2295 gtk_widget_show ( item );
2297 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_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 ( _("V_iew All Waypoints") );
2303 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_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 ( _("_Goto Center of Layer") );
2308 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), 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 _Waypoint...") );
2313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2314 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2315 gtk_widget_show ( item );
2317 export_submenu = gtk_menu_new ();
2318 item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
2319 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2320 gtk_widget_show ( item );
2321 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2323 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2324 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2325 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2326 gtk_widget_show ( item );
2328 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2329 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), 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 _GPX...") );
2334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), 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 _KML...") );
2339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), 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 ( _("_New Waypoint...") );
2344 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2345 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2346 gtk_widget_show ( item );
2348 #ifdef VIK_CONFIG_GEONAMES
2349 wikipedia_submenu = gtk_menu_new();
2350 item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2351 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2352 gtk_widget_show(item);
2353 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2355 item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2356 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2357 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2358 gtk_widget_show ( item );
2360 item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
2361 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2362 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2363 gtk_widget_show ( item );
2366 #ifdef VIK_CONFIG_OPENSTREETMAP
2367 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2369 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2370 gtk_widget_show ( item );
2373 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2374 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2376 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2377 gtk_widget_show ( item );
2380 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2381 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2383 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2384 gtk_widget_show ( item );
2388 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2390 if ( VIK_LAYER(vtl)->realized )
2392 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2394 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2397 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2398 // Visibility column always needed for waypoints
2399 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2400 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2402 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2404 // Actual setting of visibility dependent on the waypoint
2405 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2406 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter, TRUE );
2407 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2411 highest_wp_number_add_wp(vtl, name);
2412 g_hash_table_insert ( vtl->waypoints, name, wp );
2416 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2418 if ( VIK_LAYER(vtl)->realized )
2420 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2422 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2425 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2426 // Visibility column always needed for tracks
2427 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2428 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2430 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2432 // Actual setting of visibility dependent on the track
2433 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2434 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter, TRUE );
2435 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2439 g_hash_table_insert ( vtl->tracks, name, t );
2443 /* to be called whenever a track has been deleted or may have been changed. */
2444 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2446 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2447 trw_layer_cancel_current_tp ( vtl, FALSE );
2448 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2449 trw_layer_cancel_last_tp ( vtl );
2452 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2455 gchar *newname = g_strdup(name);
2456 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2457 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2458 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2460 newname = new_newname;
2466 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2468 vik_trw_layer_add_waypoint ( vtl,
2469 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2472 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2474 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
2475 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2476 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2477 vik_track_free ( tr );
2478 vtl->magic_scissors_append = FALSE; /* this means we have added it */
2480 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2481 vik_trw_layer_add_track ( vtl, new_name, tr );
2483 if ( vtl->magic_scissors_check_added_track ) {
2484 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2485 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2486 g_free ( vtl->magic_scissors_added_track_name );
2487 vtl->magic_scissors_added_track_name = g_strdup(new_name);
2492 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2494 *l = g_list_append(*l, (gpointer)name);
2497 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2499 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2500 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2502 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2503 vik_trw_layer_delete_track(vtl_src, name);
2504 vik_trw_layer_add_track(vtl_dest, newname, t);
2506 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2508 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2509 vik_trw_layer_delete_waypoint(vtl_src, name);
2510 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2514 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2516 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2517 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2519 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2520 GList *items = NULL;
2523 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2524 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2526 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2527 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2532 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2533 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2535 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2542 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2543 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2547 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2549 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2550 gboolean was_visible = FALSE;
2554 was_visible = t->visible;
2555 if ( t == vtl->current_track ) {
2556 vtl->current_track = NULL;
2558 if ( t == vtl->magic_scissors_current_track )
2559 vtl->magic_scissors_current_track = NULL;
2560 /* could be current_tp, so we have to check */
2561 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2563 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2564 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2565 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2567 /* do this last because trk_name may be pointing to actual orig key */
2568 g_hash_table_remove ( vtl->tracks, trk_name );
2573 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2575 gboolean was_visible = FALSE;
2578 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2582 if ( wp == vtl->current_wp ) {
2583 vtl->current_wp = NULL;
2584 vtl->current_wp_name = NULL;
2585 vtl->moving_wp = FALSE;
2588 was_visible = wp->visible;
2589 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2590 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2591 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2593 highest_wp_number_remove_wp(vtl, wp_name);
2594 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2600 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2602 vik_treeview_item_delete (vt, it );
2605 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2608 vtl->current_track = NULL;
2609 vtl->magic_scissors_current_track = NULL;
2610 if (vtl->current_tp_track_name)
2611 trw_layer_cancel_current_tp(vtl, FALSE);
2612 if (vtl->last_tp_track_name)
2613 trw_layer_cancel_last_tp ( vtl );
2615 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2616 g_hash_table_remove_all(vtl->tracks_iters);
2617 g_hash_table_remove_all(vtl->tracks);
2619 /* TODO: only update if the layer is visible (ticked) */
2620 vik_layer_emit_update ( VIK_LAYER(vtl) );
2623 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2625 vtl->current_wp = NULL;
2626 vtl->current_wp_name = NULL;
2627 vtl->moving_wp = FALSE;
2629 highest_wp_number_reset(vtl);
2631 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2632 g_hash_table_remove_all(vtl->waypoints_iters);
2633 g_hash_table_remove_all(vtl->waypoints);
2635 /* TODO: only update if the layer is visible (ticked) */
2636 vik_layer_emit_update ( VIK_LAYER(vtl) );
2639 static void trw_layer_delete_item ( gpointer pass_along[6] )
2641 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2642 gboolean was_visible = FALSE;
2643 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2645 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2649 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2652 vik_layer_emit_update ( VIK_LAYER(vtl) );
2656 static void trw_layer_properties_item ( gpointer pass_along[6] )
2658 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2659 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2661 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2664 gboolean updated = FALSE;
2665 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
2667 if ( updated && VIK_LAYER(vtl)->visible )
2668 vik_layer_emit_update ( VIK_LAYER(vtl) );
2673 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2676 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2678 pass_along[1], /* vlp */
2679 pass_along[3], /* track name */
2680 pass_along[5] ); /* vvp */
2686 Parameter 1 -> VikLayersPanel
2687 Parameter 2 -> VikLayer
2688 Parameter 3 -> VikViewport
2690 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2693 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2694 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2697 /* since vlp not set, vl & vvp should be valid instead! */
2699 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2700 vik_layer_emit_update ( VIK_LAYER(vl) );
2705 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
2707 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2708 if ( trps && trps->data )
2709 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2712 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
2714 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2715 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2716 if ( trps && *trps )
2718 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2720 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2721 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2722 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2723 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2724 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
2728 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2730 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2731 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2733 vtl->current_track = track;
2734 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2736 if ( track->trackpoints )
2737 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2741 * extend a track using magic scissors
2743 static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2745 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2746 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2747 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2749 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2750 vtl->magic_scissors_coord = last_coord;
2751 vtl->magic_scissors_current_track = track;
2752 vtl->magic_scissors_started = TRUE;
2754 if ( track->trackpoints )
2755 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
2759 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2761 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2762 /* Also warn if overwrite old elevation data */
2763 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2765 vik_track_apply_dem_data ( track );
2768 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2770 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2773 trps = g_list_last(trps);
2774 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2777 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
2779 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2782 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2785 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
2787 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2790 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2793 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
2795 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2798 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2802 * Automatically change the viewport to center on the track and zoom to see the extent of the track
2804 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2806 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2807 if ( trps && *trps )
2809 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2810 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2811 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
2812 if ( pass_along[1] )
2813 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2815 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
2819 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
2821 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2822 trw_layer_tpwin_init ( vtl );
2825 /*************************************
2826 * merge/split by time routines
2827 *************************************/
2829 /* called for each key in track hash table.
2830 * If the current track has time stamp, add it to the result,
2831 * except the one pointed by "exclude".
2832 * set exclude to NULL if there is no exclude to check.
2833 * Not that result is in reverse (for performance reason).
2839 static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2841 twt_udata *user_data = udata;
2842 VikTrackpoint *p1, *p2;
2844 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2848 if (VIK_TRACK(value)->trackpoints) {
2849 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2850 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2852 if (!p1->has_timestamp || !p2->has_timestamp) {
2853 g_print("no timestamp\n");
2859 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2862 /* called for each key in track hash table. if original track user_data[1] is close enough
2863 * to the passed one, add it to list in user_data[0]
2865 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2868 VikTrackpoint *p1, *p2;
2870 GList **nearby_tracks = ((gpointer *)user_data)[0];
2871 GList *orig_track = ((gpointer *)user_data)[1];
2872 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
2875 * detect reasons for not merging, and return
2876 * if no reason is found not to merge, then do it.
2879 if (VIK_TRACK(value)->trackpoints == orig_track) {
2883 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2884 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2886 if (VIK_TRACK(value)->trackpoints) {
2887 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2888 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2890 if (!p1->has_timestamp || !p2->has_timestamp) {
2891 g_print("no timestamp\n");
2895 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2896 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2898 abs(p1->timestamp - t2) < thr*60)
2905 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2908 /* comparison function used to sort tracks; a and b are hash table keys */
2909 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2911 GHashTable *tracks = user_data;
2914 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2915 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2917 if (t1 < t2) return -1;
2918 if (t1 > t2) return 1;
2922 /* comparison function used to sort trackpoints */
2923 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2925 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2927 if (t1 < t2) return -1;
2928 if (t1 > t2) return 1;
2932 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2934 * comparison function which can be used to sort tracks or waypoints by name
2936 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
2938 const gchar* namea = (const gchar*) a;
2939 const gchar* nameb = (const gchar*) b;
2940 if ( namea == NULL || nameb == NULL)
2943 // Same sort method as used in the vik_treeview_*_alphabetize functions
2944 return strcmp ( namea, nameb );
2948 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2950 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2951 gchar *orig_track_name = pass_along[3];
2952 GList *tracks_with_timestamp = NULL;
2953 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2955 if (track->trackpoints &&
2956 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2957 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2964 udata.result = &tracks_with_timestamp;
2965 udata.exclude = track->trackpoints;
2966 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2967 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2970 if (!tracks_with_timestamp) {
2971 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2975 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2976 // Sort alphabetically for user presentation
2977 tracks_with_timestamp = g_list_sort_with_data (tracks_with_timestamp, sort_alphabetically, NULL);
2980 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2981 tracks_with_timestamp, TRUE,
2982 _("Merge with..."), _("Select track to merge with"));
2983 g_list_free(tracks_with_timestamp);
2988 for (l = merge_list; l != NULL; l = g_list_next(l)) {
2989 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2991 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2992 merge_track->trackpoints = NULL;
2993 vik_trw_layer_delete_track(vtl, l->data);
2994 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2997 /* TODO: free data before free merge_list */
2998 for (l = merge_list; l != NULL; l = g_list_next(l))
3000 g_list_free(merge_list);
3001 vik_layer_emit_update( VIK_LAYER(vtl) );
3005 /* merge by time routine */
3006 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3008 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3009 gchar *orig_track_name = strdup(pass_along[3]);
3012 GList *nearby_tracks;
3015 static guint thr = 1;
3016 guint track_count = 0;
3018 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3019 _("Merge Threshold..."),
3020 _("Merge when time between tracks less than:"),
3022 free(orig_track_name);
3026 /* merge tracks until we can't */
3027 nearby_tracks = NULL;
3031 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3032 trps = track->trackpoints;
3037 if (nearby_tracks) {
3038 g_list_free(nearby_tracks);
3039 nearby_tracks = NULL;
3042 t1 = ((VikTrackpoint *)trps->data)->timestamp;
3043 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3045 /* g_print("Original track times: %d and %d\n", t1, t2); */
3046 params[0] = &nearby_tracks;
3048 params[2] = GUINT_TO_POINTER (thr);
3050 /* get a list of adjacent-in-time tracks */
3051 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3053 /* add original track */
3054 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3058 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3059 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3060 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3061 GList *l = nearby_tracks;
3062 VikTrack *tr = vik_track_new();
3063 tr->visible = track->visible;
3068 t1 = get_first_trackpoint(l)->timestamp;
3069 t2 = get_last_trackpoint(l)->timestamp;
3070 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3074 /* remove trackpoints from merged track, delete track */
3075 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3076 get_track(l)->trackpoints = NULL;
3077 vik_trw_layer_delete_track(vtl, l->data);
3082 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3083 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3085 #undef get_first_trackpoint
3086 #undef get_last_trackpoint
3089 } while (track_count > 1);
3090 g_list_free(nearby_tracks);
3091 free(orig_track_name);
3092 vik_layer_emit_update( VIK_LAYER(vtl) );
3095 /* split by time routine */
3096 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3098 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3099 GList *trps = track->trackpoints;
3101 GList *newlists = NULL;
3102 GList *newtps = NULL;
3104 static guint thr = 1;
3111 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3112 _("Split Threshold..."),
3113 _("Split when time between trackpoints exceeds:"),
3118 /* iterate through trackpoints, and copy them into new lists without touching original list */
3119 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3123 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3125 g_print("panic: ts < prev_ts: this should never happen!\n");
3128 if (ts - prev_ts > thr*60) {
3129 /* flush accumulated trackpoints into new list */
3130 newlists = g_list_append(newlists, g_list_reverse(newtps));
3134 /* accumulate trackpoint copies in newtps, in reverse order */
3135 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3137 iter = g_list_next(iter);
3140 newlists = g_list_append(newlists, g_list_reverse(newtps));
3143 /* put lists of trackpoints into tracks */
3146 // Only bother updating if the split results in new tracks
3147 if (g_list_length (newlists) > 1) {
3152 tr = vik_track_new();
3153 tr->visible = track->visible;
3154 tr->trackpoints = (GList *)(iter->data);
3156 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3157 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3158 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3159 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3161 iter = g_list_next(iter);
3163 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3164 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3166 g_list_free(newlists);
3170 * Split a track by the number of points as specified by the user
3172 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3174 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3175 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3177 // Check valid track
3178 GList *trps = track->trackpoints;
3182 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3183 _("Split Every Nth Point"),
3184 _("Split on every Nth point:"),
3185 250, // Default value as per typical limited track capacity of various GPS devices
3189 // Was a valid number returned?
3195 GList *newlists = NULL;
3196 GList *newtps = NULL;
3201 /* accumulate trackpoint copies in newtps, in reverse order */
3202 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3204 if (count >= points) {
3205 /* flush accumulated trackpoints into new list */
3206 newlists = g_list_append(newlists, g_list_reverse(newtps));
3210 iter = g_list_next(iter);
3213 // If there is a remaining chunk put that into the new split list
3214 // This may well be the whole track if no split points were encountered
3216 newlists = g_list_append(newlists, g_list_reverse(newtps));
3219 /* put lists of trackpoints into tracks */
3222 // Only bother updating if the split results in new tracks
3223 if (g_list_length (newlists) > 1) {
3228 tr = vik_track_new();
3229 tr->visible = track->visible;
3230 tr->trackpoints = (GList *)(iter->data);
3232 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3233 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3235 iter = g_list_next(iter);
3237 // Remove original track and then update the display
3238 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3239 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3241 g_list_free(newlists);
3244 /* end of split/merge routines */
3247 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3249 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3251 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3254 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3256 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3257 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3261 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3263 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3268 if (strcmp(newname, sublayer) == 0 )
3271 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3272 if (g_hash_table_lookup( l->waypoints, newname))
3274 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3279 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3280 g_hash_table_steal ( l->waypoints_iters, sublayer );
3282 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3283 highest_wp_number_remove_wp(l, sublayer);
3284 g_hash_table_remove ( l->waypoints, sublayer );
3286 rv = g_strdup(newname);
3288 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3290 highest_wp_number_add_wp(l, rv);
3291 g_hash_table_insert ( l->waypoints, rv, wp );
3292 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3294 /* it hasn't been updated yet so we pass new name */
3295 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3296 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3299 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3302 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3309 if (strcmp(newname, sublayer) == 0)
3312 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3313 if (g_hash_table_lookup( l->tracks, newname))
3315 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3320 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3321 g_hash_table_steal ( l->tracks, sublayer );
3323 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3324 g_hash_table_steal ( l->tracks_iters, sublayer );
3326 rv = g_strdup(newname);
3328 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3330 g_hash_table_insert ( l->tracks, rv, tr );
3331 g_hash_table_insert ( l->tracks_iters, rv, iter );
3333 /* don't forget about current_tp_track_name, update that too */
3334 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3336 l->current_tp_track_name = rv;
3338 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3340 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3341 l->last_tp_track_name = rv;
3343 g_free ( orig_key );
3345 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3346 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3349 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3355 static gboolean is_valid_geocache_name ( gchar *str )
3357 gint len = strlen ( str );
3358 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]));
3361 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3363 gchar *track_name = (gchar *) pass_along[3];
3364 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3365 a_acquire_set_filter_track ( tr, track_name );
3368 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3370 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3371 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3374 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3376 gchar *track_name = (gchar *) pass_along[3];
3377 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3379 gchar *escaped = uri_escape ( tr->comment );
3380 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3381 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3387 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3388 /* viewpoint is now available instead */
3389 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3391 static GtkTreeIter staticiter;
3392 static gpointer pass_along[6];
3394 gboolean rv = FALSE;
3397 pass_along[1] = vlp;
3398 pass_along[2] = GINT_TO_POINTER (subtype);
3399 pass_along[3] = sublayer;
3401 staticiter = *iter; /* will exist after function has ended */
3402 pass_along[4] = &staticiter;
3404 pass_along[5] = vvp;
3406 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3410 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3412 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3413 gtk_widget_show ( item );
3415 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3416 VikTrwLayer *vtl = l;
3417 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3418 if (tr && tr->property_dialog)
3419 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3422 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3423 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3424 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3425 gtk_widget_show ( item );
3427 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3428 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3429 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3430 gtk_widget_show ( item );
3432 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3433 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3434 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3435 gtk_widget_show ( item );
3437 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3439 gboolean separator_created = FALSE;
3441 /* could be a right-click using the tool */
3442 if ( vlp != NULL ) {
3443 item = gtk_menu_item_new ();
3444 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3445 gtk_widget_show ( item );
3447 separator_created = TRUE;
3449 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3450 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3451 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3452 gtk_widget_show ( item );
3455 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3457 if ( !separator_created ) {
3458 item = gtk_menu_item_new ();
3459 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3460 gtk_widget_show ( item );
3461 separator_created = TRUE;
3464 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3465 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3466 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3467 gtk_widget_show ( item );
3470 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
3472 if ( wp && wp->image )
3474 if ( !separator_created ) {
3475 item = gtk_menu_item_new ();
3476 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3477 gtk_widget_show ( item );
3478 separator_created = TRUE;
3481 // Set up image paramater
3482 pass_along[5] = wp->image;
3484 item = gtk_menu_item_new_with_mnemonic ( _("_Show Picture...") );
3485 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
3486 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3487 gtk_widget_show ( item );
3493 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3496 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3497 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3498 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3499 gtk_widget_show ( item );
3502 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
3504 item = gtk_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
3505 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3506 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3507 gtk_widget_show ( item );
3509 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3511 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3512 gtk_widget_show ( item );
3515 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3517 GtkWidget *goto_submenu;
3518 item = gtk_menu_item_new ();
3519 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3520 gtk_widget_show ( item );
3522 goto_submenu = gtk_menu_new ();
3523 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3524 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3525 gtk_widget_show ( item );
3526 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3528 item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
3529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3530 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3531 gtk_widget_show ( item );
3533 item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3535 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3536 gtk_widget_show ( item );
3538 item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
3539 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3540 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3541 gtk_widget_show ( item );
3543 item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3544 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3545 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3546 gtk_widget_show ( item );
3548 item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3550 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3551 gtk_widget_show ( item );
3553 item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3554 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3555 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3556 gtk_widget_show ( item );
3558 item = gtk_menu_item_new_with_mnemonic ( _("_View Track") );
3559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3560 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3561 gtk_widget_show ( item );
3563 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
3564 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3565 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3566 gtk_widget_show ( item );
3568 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3569 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3570 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3571 gtk_widget_show ( item );
3573 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
3574 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3575 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3576 gtk_widget_show ( item );
3578 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
3579 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
3580 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3581 gtk_widget_show ( item );
3583 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
3585 item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3586 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3587 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3588 gtk_widget_show ( item );
3591 item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3592 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3593 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3594 gtk_widget_show ( item );
3596 item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
3597 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3598 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3599 gtk_widget_show ( item );
3601 item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3602 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3603 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3604 gtk_widget_show ( item );
3606 item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Magic Scissors") );
3607 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
3608 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3609 gtk_widget_show ( item );
3611 #ifdef VIK_CONFIG_OPENSTREETMAP
3612 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3613 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3614 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3615 gtk_widget_show ( item );
3618 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3620 item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3621 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3622 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3623 gtk_widget_show ( item );
3626 item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
3627 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3628 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3629 gtk_widget_show ( item );
3631 /* ATM This function is only available via the layers panel, due to needing a vlp */
3633 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3634 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3635 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3637 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3638 gtk_widget_show ( item );
3642 // Only show on viewport popmenu when a trackpoint is selected
3643 if ( ! vlp && l->current_tpl ) {
3645 item = gtk_menu_item_new ();
3646 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3647 gtk_widget_show ( item );
3649 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
3650 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
3651 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
3652 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3653 gtk_widget_show ( item );
3661 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3664 if (!vtl->current_tpl)
3666 if (!vtl->current_tpl->next)
3669 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3670 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3672 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3675 VikTrackpoint *tp_new = vik_trackpoint_new();
3676 struct LatLon ll_current, ll_next;
3677 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3678 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3680 /* main positional interpolation */
3681 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3682 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3684 /* Now other properties that can be interpolated */
3685 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3687 if (tp_current->has_timestamp && tp_next->has_timestamp) {
3688 /* Note here the division is applied to each part, then added
3689 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3690 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3691 tp_new->has_timestamp = TRUE;
3694 if (tp_current->speed != NAN && tp_next->speed != NAN)
3695 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3697 /* TODO - improve interpolation of course, as it may not be correct.
3698 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3699 [similar applies if value is in radians] */
3700 if (tp_current->course != NAN && tp_next->course != NAN)
3701 tp_new->speed = (tp_current->course + tp_next->course)/2;
3703 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3705 /* Insert new point into the trackpoints list after the current TP */
3706 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3707 gint index = g_list_index ( tr->trackpoints, tp_current );
3709 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3714 /* to be called when last_tpl no long exists. */
3715 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3717 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3718 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3719 vtl->last_tpl = NULL;
3720 vtl->last_tp_track_name = NULL;
3723 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3729 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3733 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3735 if ( vtl->current_tpl )
3737 vtl->current_tpl = NULL;
3738 vtl->current_tp_track_name = NULL;
3739 vik_layer_emit_update(VIK_LAYER(vtl));
3743 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3745 g_assert ( vtl->tpwin != NULL );
3746 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3747 trw_layer_cancel_current_tp ( vtl, TRUE );
3748 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3750 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
3751 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
3753 VikTrack *tr = vik_track_new ();
3754 GList *newglist = g_list_alloc ();
3755 newglist->prev = NULL;
3756 newglist->next = vtl->current_tpl->next;
3757 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3758 tr->trackpoints = newglist;
3760 vtl->current_tpl->next->prev = newglist; /* end old track here */
3761 vtl->current_tpl->next = NULL;
3763 vtl->current_tpl = newglist; /* change tp to first of new track. */
3764 vtl->current_tp_track_name = name;
3766 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3770 vik_trw_layer_add_track ( vtl, name, tr );
3771 vik_layer_emit_update(VIK_LAYER(vtl));
3774 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3776 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3778 g_assert(tr != NULL);
3780 /* can't join with a non-existent trackpoint */
3781 vtl->last_tpl = NULL;
3782 vtl->last_tp_track_name = NULL;
3784 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3786 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3787 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3789 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3791 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3792 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3794 trw_layer_cancel_last_tp ( vtl );
3796 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3797 g_list_free_1 ( vtl->current_tpl );
3798 vtl->current_tpl = new_tpl;
3799 vik_layer_emit_update(VIK_LAYER(vtl));
3803 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3804 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3805 g_list_free_1 ( vtl->current_tpl );
3806 trw_layer_cancel_current_tp ( vtl, FALSE );
3809 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3811 vtl->last_tpl = vtl->current_tpl;
3812 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3813 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3815 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3817 vtl->last_tpl = vtl->current_tpl;
3818 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3819 vik_layer_emit_update(VIK_LAYER(vtl));
3821 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
3823 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
3824 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3826 VikTrack *tr_first = tr1, *tr_last = tr2;
3830 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
3831 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
3832 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
3833 vik_track_reverse ( tr1 );
3834 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
3839 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
3841 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. */
3842 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
3843 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
3844 tr2->trackpoints = NULL;
3846 tmp = vtl->current_tp_track_name;
3848 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
3849 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3851 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
3852 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
3853 vik_trw_layer_delete_track ( vtl, tmp );
3855 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
3856 vik_layer_emit_update(VIK_LAYER(vtl));
3858 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
3860 trw_layer_insert_tp_after_current_tp ( vtl );
3861 vik_layer_emit_update(VIK_LAYER(vtl));
3863 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
3864 vik_layer_emit_update (VIK_LAYER(vtl));
3867 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
3871 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
3872 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
3873 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
3874 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
3875 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
3877 if ( vtl->current_tpl )
3878 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3879 /* set layer name and TP data */
3882 /***************************************************************************
3884 ***************************************************************************/
3886 /*** Utility data structures and functions ****/
3890 gint closest_x, closest_y;
3891 gchar *closest_wp_name;
3892 VikWaypoint *closest_wp;
3898 gint closest_x, closest_y;
3899 gchar *closest_track_name;
3900 VikTrackpoint *closest_tp;
3905 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
3911 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
3913 // If waypoint has an image then use the image size to select
3915 gint slackx, slacky;
3916 slackx = wp->image_width / 2;
3917 slacky = wp->image_height / 2;
3919 if ( x <= params->x + slackx && x >= params->x - slackx
3920 && y <= params->y + slacky && y >= params->y - slacky ) {
3921 params->closest_wp_name = name;
3922 params->closest_wp = wp;
3923 params->closest_x = x;
3924 params->closest_y = y;
3927 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
3928 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
3929 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3931 params->closest_wp_name = name;
3932 params->closest_wp = wp;
3933 params->closest_x = x;
3934 params->closest_y = y;
3938 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
3940 GList *tpl = t->trackpoints;
3949 tp = VIK_TRACKPOINT(tpl->data);
3951 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
3953 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
3954 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
3955 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3957 params->closest_track_name = name;
3958 params->closest_tp = tp;
3959 params->closest_tpl = tpl;
3960 params->closest_x = x;
3961 params->closest_y = y;
3967 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3969 TPSearchParams params;
3973 params.closest_track_name = NULL;
3974 params.closest_tp = NULL;
3975 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3976 return params.closest_tp;
3979 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3981 WPSearchParams params;
3985 params.closest_wp = NULL;
3986 params.closest_wp_name = NULL;
3987 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3988 return params.closest_wp;
3991 // Some forward declarations
3992 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
3993 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
3994 static void marker_end_move ( tool_ed_t *t );
3997 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4001 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4003 // Here always allow snapping back to the original location
4004 // this is useful when one decides not to move the thing afterall
4005 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4008 if ( event->state & GDK_CONTROL_MASK )
4010 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4012 new_coord = tp->coord;
4016 if ( event->state & GDK_SHIFT_MASK )
4018 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4020 new_coord = wp->coord;
4024 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4026 marker_moveto ( t, x, y );
4033 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4035 if ( t->holding && event->button == 1 )
4038 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4041 if ( event->state & GDK_CONTROL_MASK )
4043 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4045 new_coord = tp->coord;
4049 if ( event->state & GDK_SHIFT_MASK )
4051 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4053 new_coord = wp->coord;
4056 marker_end_move ( t );
4058 // Determine if working on a waypoint or a trackpoint
4059 if ( t->is_waypoint )
4060 vtl->current_wp->coord = new_coord;
4062 if ( vtl->current_tpl ) {
4063 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4066 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4068 // Don't really know what this is for but seems like it might be handy...
4069 /* can't join with itself! */
4070 trw_layer_cancel_last_tp ( vtl );
4075 vtl->current_wp = NULL;
4076 vtl->current_wp_name = NULL;
4077 trw_layer_cancel_current_tp ( vtl, FALSE );
4079 vik_layer_emit_update ( VIK_LAYER(vtl) );
4086 Returns true if a waypoint or track is found near the requested event position for this particular layer
4087 The item found is automatically selected
4088 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4090 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4092 if ( event->button != 1 )
4095 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4098 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4101 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4103 if (vtl->waypoints_visible) {
4104 WPSearchParams wp_params;
4105 wp_params.vvp = vvp;
4106 wp_params.x = event->x;
4107 wp_params.y = event->y;
4108 wp_params.closest_wp_name = NULL;
4109 wp_params.closest_wp = NULL;
4111 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4113 if ( wp_params.closest_wp ) {
4116 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4118 // Too easy to move it so must be holding shift to start immediately moving it
4119 // or otherwise be previously selected
4120 if ( event->state & GDK_SHIFT_MASK ||
4121 vtl->current_wp == wp_params.closest_wp ) {
4122 // Put into 'move buffer'
4123 // NB vvp & vw already set in tet
4124 tet->vtl = (gpointer)vtl;
4125 tet->is_waypoint = TRUE;
4127 marker_begin_move (tet, event->x, event->y);
4130 vtl->current_wp = wp_params.closest_wp;
4131 vtl->current_wp_name = wp_params.closest_wp_name;
4133 vik_layer_emit_update ( VIK_LAYER(vtl) );
4139 if (vtl->tracks_visible) {
4140 TPSearchParams tp_params;
4141 tp_params.vvp = vvp;
4142 tp_params.x = event->x;
4143 tp_params.y = event->y;
4144 tp_params.closest_track_name = NULL;
4145 tp_params.closest_tp = NULL;
4147 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4149 if ( tp_params.closest_tp ) {
4151 // Always select + highlight the track
4152 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4154 tet->is_waypoint = FALSE;
4156 // Select the Trackpoint
4157 // Can move it immediately when control held or it's the previously selected tp
4158 if ( event->state & GDK_CONTROL_MASK ||
4159 vtl->current_tpl == tp_params.closest_tpl ) {
4160 // Put into 'move buffer'
4161 // NB vvp & vw already set in tet
4162 tet->vtl = (gpointer)vtl;
4163 marker_begin_move (tet, event->x, event->y);
4166 vtl->current_tpl = tp_params.closest_tpl;
4167 vtl->current_tp_track_name = tp_params.closest_track_name;
4170 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4172 vik_layer_emit_update ( VIK_LAYER(vtl) );
4177 /* these aren't the droids you're looking for */
4178 vtl->current_wp = NULL;
4179 vtl->current_wp_name = NULL;
4180 trw_layer_cancel_current_tp ( vtl, FALSE );
4185 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4187 if ( event->button != 3 )
4190 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4193 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4196 /* Post menu for the currently selected item */
4198 /* See if a track is selected */
4199 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4200 if ( track && track->visible ) {
4202 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4204 if ( vtl->track_right_click_menu )
4205 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4207 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4209 vik_trw_layer_sublayer_add_menu_items ( vtl,
4210 vtl->track_right_click_menu,
4212 VIK_TRW_LAYER_SUBLAYER_TRACK,
4213 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4214 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4217 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4223 /* See if a waypoint is selected */
4224 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4225 if ( waypoint && waypoint->visible ) {
4226 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4228 if ( vtl->wp_right_click_menu )
4229 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4231 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4232 vik_trw_layer_sublayer_add_menu_items ( vtl,
4233 vtl->wp_right_click_menu,
4235 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4236 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4237 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4239 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4248 /* background drawing hook, to be passed the viewport */
4249 static gboolean tool_sync_done = TRUE;
4251 static gboolean tool_sync(gpointer data)
4253 VikViewport *vvp = data;
4254 gdk_threads_enter();
4255 vik_viewport_sync(vvp);
4256 tool_sync_done = TRUE;
4257 gdk_threads_leave();
4261 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4264 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4265 gdk_gc_set_function ( t->gc, GDK_INVERT );
4266 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4267 vik_viewport_sync(t->vvp);
4272 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4274 VikViewport *vvp = t->vvp;
4275 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4276 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4280 if (tool_sync_done) {
4281 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4282 tool_sync_done = FALSE;
4286 static void marker_end_move ( tool_ed_t *t )
4288 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4289 g_object_unref ( t->gc );
4293 /*** Edit waypoint ****/
4295 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4297 tool_ed_t *t = g_new(tool_ed_t, 1);
4303 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4305 WPSearchParams params;
4306 tool_ed_t *t = data;
4307 VikViewport *vvp = t->vvp;
4309 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4316 if ( !vtl->vl.visible || !vtl->waypoints_visible )
4319 if ( vtl->current_wp && vtl->current_wp->visible )
4321 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
4323 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
4325 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
4326 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
4328 if ( event->button == 3 )
4329 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4331 marker_begin_move(t, event->x, event->y);
4338 params.x = event->x;
4339 params.y = event->y;
4340 params.closest_wp_name = NULL;
4341 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4342 params.closest_wp = NULL;
4343 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4344 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
4346 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
4347 marker_begin_move(t, event->x, event->y);
4348 g_critical("shouldn't be here");
4351 else if ( params.closest_wp )
4353 if ( event->button == 3 )
4354 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4356 vtl->waypoint_rightclick = FALSE;
4358 vtl->current_wp = params.closest_wp;
4359 vtl->current_wp_name = params.closest_wp_name;
4361 if ( params.closest_wp )
4362 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ), TRUE );
4364 /* could make it so don't update if old WP is off screen and new is null but oh well */
4365 vik_layer_emit_update ( VIK_LAYER(vtl) );
4369 vtl->current_wp = NULL;
4370 vtl->current_wp_name = NULL;
4371 vtl->waypoint_rightclick = FALSE;
4372 vik_layer_emit_update ( VIK_LAYER(vtl) );
4376 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4378 tool_ed_t *t = data;
4379 VikViewport *vvp = t->vvp;
4381 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4386 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4389 if ( event->state & GDK_CONTROL_MASK )
4391 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4393 new_coord = tp->coord;
4397 if ( event->state & GDK_SHIFT_MASK )
4399 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4400 if ( wp && wp != vtl->current_wp )
4401 new_coord = wp->coord;
4406 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4408 marker_moveto ( t, x, y );
4415 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4417 tool_ed_t *t = data;
4418 VikViewport *vvp = t->vvp;
4420 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4423 if ( t->holding && event->button == 1 )
4426 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4429 if ( event->state & GDK_CONTROL_MASK )
4431 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4433 new_coord = tp->coord;
4437 if ( event->state & GDK_SHIFT_MASK )
4439 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4440 if ( wp && wp != vtl->current_wp )
4441 new_coord = wp->coord;
4444 marker_end_move ( t );
4446 vtl->current_wp->coord = new_coord;
4447 vik_layer_emit_update ( VIK_LAYER(vtl) );
4450 /* PUT IN RIGHT PLACE!!! */
4451 if ( event->button == 3 && vtl->waypoint_rightclick )
4453 if ( vtl->wp_right_click_menu )
4454 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
4455 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4456 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 );
4457 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4458 vtl->waypoint_rightclick = FALSE;
4463 /**** Begin track ***/
4464 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4469 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4471 vtl->current_track = NULL;
4472 return tool_new_track_click ( vtl, event, vvp );
4475 /*** New track ****/
4477 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4486 } new_track_move_passalong_t;
4488 /* sync and undraw, but only when we have time */
4489 static gboolean ct_sync ( gpointer passalong )
4491 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4492 vik_viewport_sync ( p->vvp );
4493 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4494 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4495 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4496 p->vtl->ct_sync_done = TRUE;
4501 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
4503 /* if we haven't sync'ed yet, we don't have time to do more. */
4504 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
4505 GList *iter = vtl->current_track->trackpoints;
4506 new_track_move_passalong_t *passalong;
4509 while ( iter->next )
4511 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
4512 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
4513 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
4514 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
4516 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
4517 passalong->vtl = vtl;
4518 passalong->vvp = vvp;
4521 passalong->x2 = event->x;
4522 passalong->y2 = event->y;
4524 /* this will sync and undraw when we have time to */
4525 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
4526 vtl->ct_sync_done = FALSE;
4527 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
4529 return VIK_LAYER_TOOL_ACK;
4532 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
4534 if ( vtl->current_track && event->keyval == GDK_Escape ) {
4535 vtl->current_track = NULL;
4536 vik_layer_emit_update ( VIK_LAYER(vtl) );
4538 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
4540 if ( vtl->current_track->trackpoints )
4542 GList *last = g_list_last(vtl->current_track->trackpoints);
4543 g_free ( last->data );
4544 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4546 vik_layer_emit_update ( VIK_LAYER(vtl) );
4552 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4556 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4559 if ( event->button == 3 && vtl->current_track )
4562 if ( vtl->current_track->trackpoints )
4564 GList *last = g_list_last(vtl->current_track->trackpoints);
4565 g_free ( last->data );
4566 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4568 vik_layer_emit_update ( VIK_LAYER(vtl) );
4572 if ( event->type == GDK_2BUTTON_PRESS )
4574 /* subtract last (duplicate from double click) tp then end */
4575 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
4577 GList *last = g_list_last(vtl->current_track->trackpoints);
4578 g_free ( last->data );
4579 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4580 /* undo last, then end */
4581 vtl->current_track = NULL;
4583 vik_layer_emit_update ( VIK_LAYER(vtl) );
4587 if ( ! vtl->current_track )
4589 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
4590 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
4592 vtl->current_track = vik_track_new();
4593 vtl->current_track->visible = TRUE;
4594 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
4596 /* incase it was created by begin track */
4597 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
4602 tp = vik_trackpoint_new();
4603 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
4605 /* snap to other TP */
4606 if ( event->state & GDK_CONTROL_MASK )
4608 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4610 tp->coord = other_tp->coord;
4613 tp->newsegment = FALSE;
4614 tp->has_timestamp = FALSE;
4616 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
4618 vtl->ct_x1 = vtl->ct_x2;
4619 vtl->ct_y1 = vtl->ct_y2;
4620 vtl->ct_x2 = event->x;
4621 vtl->ct_y2 = event->y;
4623 vik_layer_emit_update ( VIK_LAYER(vtl) );
4627 /*** New waypoint ****/
4629 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4634 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4637 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4639 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
4640 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
4641 vik_layer_emit_update ( VIK_LAYER(vtl) );
4646 /*** Edit trackpoint ****/
4648 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
4650 tool_ed_t *t = g_new(tool_ed_t, 1);
4656 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4658 tool_ed_t *t = data;
4659 VikViewport *vvp = t->vvp;
4660 TPSearchParams params;
4661 /* OUTDATED DOCUMENTATION:
4662 find 5 pixel range on each side. then put these UTM, and a pointer
4663 to the winning track name (and maybe the winning track itself), and a
4664 pointer to the winning trackpoint, inside an array or struct. pass
4665 this along, do a foreach on the tracks which will do a foreach on the
4668 params.x = event->x;
4669 params.y = event->y;
4670 params.closest_track_name = NULL;
4671 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4672 params.closest_tp = NULL;
4674 if ( event->button != 1 )
4677 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4680 if ( !vtl->vl.visible || !vtl->tracks_visible )
4683 if ( vtl->current_tpl )
4685 /* 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.) */
4686 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
4687 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
4689 g_assert ( current_tr );
4691 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
4693 if ( current_tr->visible &&
4694 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
4695 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
4696 marker_begin_move ( t, event->x, event->y );
4700 vtl->last_tpl = vtl->current_tpl;
4701 vtl->last_tp_track_name = vtl->current_tp_track_name;
4704 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4706 if ( params.closest_tp )
4708 vtl->current_tpl = params.closest_tpl;
4709 vtl->current_tp_track_name = params.closest_track_name;
4710 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ), TRUE );
4711 trw_layer_tpwin_init ( vtl );
4712 vik_layer_emit_update ( VIK_LAYER(vtl) );
4716 /* these aren't the droids you're looking for */
4720 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4722 tool_ed_t *t = data;
4723 VikViewport *vvp = t->vvp;
4725 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4731 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4734 if ( event->state & GDK_CONTROL_MASK )
4736 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4737 if ( tp && tp != vtl->current_tpl->data )
4738 new_coord = tp->coord;
4740 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4743 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4744 marker_moveto ( t, x, y );
4752 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4754 tool_ed_t *t = data;
4755 VikViewport *vvp = t->vvp;
4757 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4759 if ( event->button != 1)
4764 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4767 if ( event->state & GDK_CONTROL_MASK )
4769 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4770 if ( tp && tp != vtl->current_tpl->data )
4771 new_coord = tp->coord;
4774 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4776 marker_end_move ( t );
4778 /* diff dist is diff from orig */
4779 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4780 /* can't join with itself! */
4781 trw_layer_cancel_last_tp ( vtl );
4783 vik_layer_emit_update ( VIK_LAYER(vtl) );
4790 /*** Magic Scissors ***/
4791 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
4796 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4799 if ( !vtl ) return FALSE;
4800 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
4801 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
4803 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
4805 vtl->magic_scissors_coord = *new_end;
4807 vik_layer_emit_update ( VIK_LAYER(vtl) );
4808 /* remove last ' to:...' */
4809 if ( vtl->magic_scissors_current_track->comment ) {
4810 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
4811 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
4812 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
4813 last_to - vtl->magic_scissors_current_track->comment - 1);
4814 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4819 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
4820 struct LatLon start, end;
4821 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
4822 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
4825 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
4826 vik_coord_to_latlon ( &(tmp), &end );
4827 vtl->magic_scissors_coord = tmp; /* for continuations */
4829 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
4830 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
4831 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
4833 vtl->magic_scissors_check_added_track = TRUE;
4834 vtl->magic_scissors_started = FALSE;
4837 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
4838 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
4839 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
4840 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
4841 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
4842 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
4845 /* see if anything was done -- a track was added or appended to */
4846 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
4849 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
4852 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
4854 vtl->magic_scissors_current_track = tr;
4856 g_free ( vtl->magic_scissors_added_track_name );
4857 vtl->magic_scissors_added_track_name = NULL;
4858 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
4859 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
4860 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
4861 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4863 vtl->magic_scissors_check_added_track = FALSE;
4864 vtl->magic_scissors_append = FALSE;
4866 vik_layer_emit_update ( VIK_LAYER(vtl) );
4868 vtl->magic_scissors_started = TRUE;
4869 vtl->magic_scissors_coord = tmp;
4870 vtl->magic_scissors_current_track = NULL;
4875 /*** Show picture ****/
4877 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
4882 /* Params are: vvp, event, last match found or NULL */
4883 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
4885 if ( wp->image && wp->visible )
4887 gint x, y, slackx, slacky;
4888 GdkEventButton *event = (GdkEventButton *) params[1];
4890 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
4891 slackx = wp->image_width / 2;
4892 slacky = wp->image_height / 2;
4893 if ( x <= event->x + slackx && x >= event->x - slackx
4894 && y <= event->y + slacky && y >= event->y - slacky )
4896 params[2] = wp->image; /* we've found a match. however continue searching
4897 * since we want to find the last match -- that
4898 * is, the match that was drawn last. */
4903 static void trw_layer_show_picture ( gpointer pass_along[6] )
4905 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
4907 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
4910 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
4911 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
4912 g_free ( quoted_file );
4913 if ( ! g_spawn_command_line_async ( cmd, &err ) )
4915 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch eog to open file.") );
4916 g_error_free ( err );
4919 #endif /* WINDOWS */
4922 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4924 gpointer params[3] = { vvp, event, NULL };
4925 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4927 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
4930 static gpointer pass_along[6];
4931 pass_along[0] = vtl;
4932 pass_along[5] = params[2];
4933 trw_layer_show_picture ( pass_along );
4934 return TRUE; /* found a match */
4937 return FALSE; /* go through other layers, searching for a match */
4940 /***************************************************************************
4942 ***************************************************************************/
4948 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
4950 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
4951 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
4954 /* Structure for thumbnail creating data used in the background thread */
4956 VikTrwLayer *vtl; // Layer needed for redrawing
4957 GSList *pics; // Image list
4958 } thumbnail_create_thread_data;
4960 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
4962 guint total = g_slist_length(tctd->pics), done = 0;
4963 while ( tctd->pics )
4965 a_thumbnails_create ( (gchar *) tctd->pics->data );
4966 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
4968 return -1; /* Abort thread */
4970 tctd->pics = tctd->pics->next;
4973 // Redraw to show the thumbnails as they are now created
4974 gdk_threads_enter();
4975 if ( IS_VIK_LAYER(tctd->vtl) )
4976 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) );
4977 gdk_threads_leave();
4982 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
4984 while ( tctd->pics )
4986 g_free ( tctd->pics->data );
4987 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
4992 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
4994 if ( ! vtl->has_verified_thumbnails )
4996 GSList *pics = NULL;
4997 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5000 gint len = g_slist_length ( pics );
5001 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5002 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5005 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5007 (vik_thr_func) create_thumbnails_thread,
5009 (vik_thr_free_func) thumbnail_create_thread_free,
5017 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5019 return vtl->coord_mode;
5024 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5026 vik_coord_convert ( &(wp->coord), *dest_mode );
5029 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5031 vik_track_convert ( tr, *dest_mode );
5034 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5036 if ( vtl->coord_mode != dest_mode )
5038 vtl->coord_mode = dest_mode;
5039 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5040 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5044 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5046 return g_hash_table_lookup ( vtl->waypoints, name );
5049 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5051 return g_hash_table_lookup ( vtl->tracks, name );
5054 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
5056 vtl->menu_selection = selection;
5059 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
5061 return(vtl->menu_selection);
5064 /* ----------- Downloading maps along tracks --------------- */
5066 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5068 /* TODO: calculating based on current size of viewport */
5069 const gdouble w_at_zoom_0_125 = 0.0013;
5070 const gdouble h_at_zoom_0_125 = 0.0011;
5071 gdouble zoom_factor = zoom_level/0.125;
5073 wh->lat = h_at_zoom_0_125 * zoom_factor;
5074 wh->lon = w_at_zoom_0_125 * zoom_factor;
5076 return 0; /* all OK */
5079 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5081 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5082 (dist->lat >= ABS(to->north_south - from->north_south)))
5085 VikCoord *coord = g_malloc(sizeof(VikCoord));
5086 coord->mode = VIK_COORD_LATLON;
5088 if (ABS(gradient) < 1) {
5089 if (from->east_west > to->east_west)
5090 coord->east_west = from->east_west - dist->lon;
5092 coord->east_west = from->east_west + dist->lon;
5093 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5095 if (from->north_south > to->north_south)
5096 coord->north_south = from->north_south - dist->lat;
5098 coord->north_south = from->north_south + dist->lat;
5099 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5105 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5107 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5108 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5110 VikCoord *next = from;
5112 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5114 list = g_list_prepend(list, next);
5120 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5122 typedef struct _Rect {
5127 #define GLRECT(iter) ((Rect *)((iter)->data))
5130 GList *rects_to_download = NULL;
5133 if (get_download_area_width(vvp, zoom_level, &wh))
5136 GList *iter = tr->trackpoints;
5140 gboolean new_map = TRUE;
5141 VikCoord *cur_coord, tl, br;
5144 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5146 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5147 rect = g_malloc(sizeof(Rect));
5150 rect->center = *cur_coord;
5151 rects_to_download = g_list_prepend(rects_to_download, rect);
5156 gboolean found = FALSE;
5157 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5158 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
5169 GList *fillins = NULL;
5170 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
5171 /* seems that ATM the function get_next_coord works only for LATLON */
5172 if ( cur_coord->mode == VIK_COORD_LATLON ) {
5173 /* fill-ins for far apart points */
5174 GList *cur_rect, *next_rect;
5175 for (cur_rect = rects_to_download;
5176 (next_rect = cur_rect->next) != NULL;
5177 cur_rect = cur_rect->next) {
5178 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
5179 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
5180 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
5184 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
5187 GList *iter = fillins;
5189 cur_coord = (VikCoord *)(iter->data);
5190 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5191 rect = g_malloc(sizeof(Rect));
5194 rect->center = *cur_coord;
5195 rects_to_download = g_list_prepend(rects_to_download, rect);
5200 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5201 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
5205 for (iter = fillins; iter; iter = iter->next)
5207 g_list_free(fillins);
5209 if (rects_to_download) {
5210 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
5211 g_free(rect_iter->data);
5212 g_list_free(rects_to_download);
5216 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
5219 gint selected_map, default_map;
5220 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
5221 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
5222 gint selected_zoom, default_zoom;
5226 VikTrwLayer *vtl = pass_along[0];
5227 VikLayersPanel *vlp = pass_along[1];
5228 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5229 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
5231 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
5232 int num_maps = g_list_length(vmls);
5235 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
5239 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
5240 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
5242 gchar **np = map_names;
5243 VikMapsLayer **lp = map_layers;
5244 for (i = 0; i < num_maps; i++) {
5245 gboolean dup = FALSE;
5246 vml = (VikMapsLayer *)(vmls->data);
5247 for (j = 0; j < i; j++) { /* no duplicate allowed */
5248 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
5255 *np++ = vik_maps_layer_get_map_label(vml);
5261 num_maps = lp - map_layers;
5263 for (default_map = 0; default_map < num_maps; default_map++) {
5264 /* TODO: check for parent layer's visibility */
5265 if (VIK_LAYER(map_layers[default_map])->visible)
5268 default_map = (default_map == num_maps) ? 0 : default_map;
5270 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
5271 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
5272 if (cur_zoom == zoom_vals[default_zoom])
5275 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
5277 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
5280 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
5283 for (i = 0; i < num_maps; i++)
5284 g_free(map_names[i]);
5292 /**** lowest waypoint number calculation ***/
5293 static gint highest_wp_number_name_to_number(const gchar *name) {
5294 if ( strlen(name) == 3 ) {
5296 if ( n < 100 && name[0] != '0' )
5298 if ( n < 10 && name[0] != '0' )
5306 static void highest_wp_number_reset(VikTrwLayer *vtl)
5308 vtl->highest_wp_number = -1;
5311 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
5313 /* if is bigger that top, add it */
5314 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
5315 if ( new_wp_num > vtl->highest_wp_number )
5316 vtl->highest_wp_number = new_wp_num;
5319 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
5321 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
5322 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
5323 if ( vtl->highest_wp_number == old_wp_num ) {
5325 vtl->highest_wp_number --;
5327 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5328 /* search down until we find something that *does* exist */
5330 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
5331 vtl->highest_wp_number --;
5332 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5337 /* get lowest unused number */
5338 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
5341 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
5343 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
5344 return g_strdup(buf);