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;
187 VikStdLayerMenuItem menu_selection;
189 gint highest_wp_number;
192 /* A caached waypoint image. */
195 gchar *image; /* filename */
198 struct DrawingParams {
202 guint16 width, height;
203 const VikCoord *center;
205 gboolean one_zone, lat_lon;
206 gdouble ce1, ce2, cn1, cn2;
209 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16);
210 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl);
212 static void trw_layer_delete_item ( gpointer pass_along[6] );
213 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
214 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
216 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
217 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
218 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
220 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
221 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
223 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
224 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
225 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
227 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
228 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
230 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
231 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
232 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
233 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
234 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
235 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
236 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
237 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
238 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
239 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
240 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
241 static void trw_layer_download_map_along_track_cb (gpointer pass_along[6]);
242 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
243 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
244 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
245 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
246 static void trw_layer_new_wp ( gpointer lav[2] );
247 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
248 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
249 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
250 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
253 static void trw_layer_properties_item ( gpointer pass_along[6] );
254 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
255 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
257 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
258 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
259 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
262 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
263 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
265 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
266 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
268 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
269 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
271 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
272 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
273 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
274 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
276 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
277 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
278 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
279 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
280 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
282 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
283 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
284 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
285 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
286 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
287 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
288 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
289 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
290 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
291 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
292 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
293 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
294 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
295 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
296 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
297 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
298 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
299 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
300 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
301 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
304 static void cached_pixbuf_free ( CachedPixbuf *cp );
305 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
306 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
308 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
309 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
311 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
313 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
314 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
315 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
317 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
318 static void highest_wp_number_reset(VikTrwLayer *vtl);
319 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
320 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
323 static VikToolInterface trw_layer_tools[] = {
324 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
325 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
327 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
328 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
329 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
331 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
332 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
334 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
335 (VikToolMouseFunc) tool_edit_waypoint_click,
336 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
337 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
339 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
340 (VikToolMouseFunc) tool_edit_trackpoint_click,
341 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
342 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
344 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
345 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
347 { N_("Magic Scissors"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
348 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
350 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
352 /****** PARAMETERS ******/
354 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
355 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
357 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
358 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
361 static VikLayerParamScale params_scales[] = {
362 /* min max step digits */
363 { 1, 10, 1, 0 }, /* line_thickness */
364 { 0.0, 99.0, 1, 2 }, /* velocity_min */
365 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
366 /* 5 * step == how much to turn */
367 { 16, 128, 3.2, 0 }, /* image_size */
368 { 0, 255, 5, 0 }, /* image alpha */
369 { 5, 500, 5, 0 }, /* image cache_size */
370 { 0, 8, 1, 0 }, /* image cache_size */
371 { 1, 64, 1, 0 }, /* wpsize */
372 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
373 { 1, 100, 1, 0 }, /* stop_length */
376 VikLayerParam trw_layer_params[] = {
377 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
378 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
380 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
381 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
382 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
383 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
384 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
386 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
387 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
389 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
390 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
391 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
392 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
393 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
395 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
396 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
397 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
398 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
399 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
400 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
401 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
402 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
404 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
405 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
406 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
407 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
410 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 };
413 *** 1) Add to trw_layer_params and enumeration
414 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
417 /****** END PARAMETERS ******/
419 VikLayerInterface vik_trw_layer_interface = {
424 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
428 params_groups, /* params_groups */
429 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
433 (VikLayerFuncCreate) vik_trw_layer_create,
434 (VikLayerFuncRealize) vik_trw_layer_realize,
435 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
436 (VikLayerFuncFree) vik_trw_layer_free,
438 (VikLayerFuncProperties) NULL,
439 (VikLayerFuncDraw) vik_trw_layer_draw,
440 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
442 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
443 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
445 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
446 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
448 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
449 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
450 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
451 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
452 (VikLayerFuncLayerSelected) vik_trw_layer_selected,
454 (VikLayerFuncMarshall) trw_layer_marshall,
455 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
457 (VikLayerFuncSetParam) trw_layer_set_param,
458 (VikLayerFuncGetParam) trw_layer_get_param,
460 (VikLayerFuncReadFileData) a_gpspoint_read_file,
461 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
463 (VikLayerFuncDeleteItem) trw_layer_del_item,
464 (VikLayerFuncCutItem) trw_layer_cut_item,
465 (VikLayerFuncCopyItem) trw_layer_copy_item,
466 (VikLayerFuncPasteItem) trw_layer_paste_item,
467 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
469 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
472 /* for copy & paste (I think?) */
480 GType vik_trw_layer_get_type ()
482 static GType vtl_type = 0;
486 static const GTypeInfo vtl_info =
488 sizeof (VikTrwLayerClass),
489 NULL, /* base_init */
490 NULL, /* base_finalize */
491 NULL, /* class init */
492 NULL, /* class_finalize */
493 NULL, /* class_data */
494 sizeof (VikTrwLayer),
496 NULL /* instance init */
498 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
504 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
506 static gpointer pass_along[6];
512 pass_along[1] = NULL;
513 pass_along[2] = GINT_TO_POINTER (subtype);
514 pass_along[3] = sublayer;
515 pass_along[4] = NULL;
516 pass_along[5] = NULL;
518 trw_layer_delete_item ( pass_along );
521 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
523 static gpointer pass_along[6];
529 pass_along[1] = NULL;
530 pass_along[2] = GINT_TO_POINTER (subtype);
531 pass_along[3] = sublayer;
532 pass_along[4] = NULL;
533 pass_along[5] = NULL;
535 trw_layer_copy_item_cb(pass_along);
536 trw_layer_cut_item_cb(pass_along);
539 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
541 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
542 gint subtype = GPOINTER_TO_INT (pass_along[2]);
543 gpointer * sublayer = pass_along[3];
547 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
550 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
555 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
557 trw_layer_copy_item_cb(pass_along);
558 trw_layer_delete_item(pass_along);
561 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
572 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
574 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
576 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
579 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
580 fi = g_malloc ( *len );
581 fi->len = strlen(sublayer) + 1;
582 memcpy(fi->data, sublayer, fi->len);
583 memcpy(fi->data + fi->len, id, il);
585 *item = (guint8 *)fi;
588 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
590 FlatItem *fi = (FlatItem *) item;
592 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
597 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
598 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
599 vik_trw_layer_add_waypoint ( vtl, name, w );
600 waypoint_convert(name, w, &vtl->coord_mode);
601 // Consider if redraw necessary for the new item
602 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
603 vik_layer_emit_update ( VIK_LAYER(vtl) );
606 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
610 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
611 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
612 vik_trw_layer_add_track ( vtl, name, t );
613 track_convert(name, t, &vtl->coord_mode);
614 // Consider if redraw necessary for the new item
615 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
616 vik_layer_emit_update ( VIK_LAYER(vtl) );
622 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
629 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
633 case PARAM_TV: vtl->tracks_visible = data.b; break;
634 case PARAM_WV: vtl->waypoints_visible = data.b; break;
635 case PARAM_DM: vtl->drawmode = data.u; break;
636 case PARAM_DP: vtl->drawpoints = data.b; break;
637 case PARAM_DE: vtl->drawelevation = data.b; break;
638 case PARAM_DS: vtl->drawstops = data.b; break;
639 case PARAM_DL: vtl->drawlines = data.b; break;
640 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
641 vtl->stop_length = data.u;
643 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
644 vtl->elevation_factor = data.u;
646 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
648 vtl->line_thickness = data.u;
649 trw_layer_new_track_gcs ( vtl, vp );
652 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
654 vtl->bg_line_thickness = data.u;
655 trw_layer_new_track_gcs ( vtl, vp );
660 /* Convert to store internally
661 NB file operation always in internal units (metres per second) */
662 vik_units_speed_t speed_units = a_vik_get_units_speed ();
663 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
664 vtl->velocity_min = data.d;
665 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
666 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
667 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
668 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
671 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
676 /* Convert to store internally
677 NB file operation always in internal units (metres per second) */
678 vik_units_speed_t speed_units = a_vik_get_units_speed ();
679 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
680 vtl->velocity_max = data.d;
681 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
682 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
683 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
684 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
687 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
690 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
691 case PARAM_DLA: vtl->drawlabels = data.b; break;
692 case PARAM_DI: vtl->drawimages = data.b; break;
693 case PARAM_IS: if ( data.u != vtl->image_size )
695 vtl->image_size = data.u;
696 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
697 g_queue_free ( vtl->image_cache );
698 vtl->image_cache = g_queue_new ();
701 case PARAM_IA: vtl->image_alpha = data.u; break;
702 case PARAM_ICS: vtl->image_cache_size = data.u;
703 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
704 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
706 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
707 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
708 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
709 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
710 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
711 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
712 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
717 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
719 VikLayerParamData rv;
722 case PARAM_TV: rv.b = vtl->tracks_visible; break;
723 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
724 case PARAM_DM: rv.u = vtl->drawmode; break;
725 case PARAM_DP: rv.b = vtl->drawpoints; break;
726 case PARAM_DE: rv.b = vtl->drawelevation; break;
727 case PARAM_EF: rv.u = vtl->elevation_factor; break;
728 case PARAM_DS: rv.b = vtl->drawstops; break;
729 case PARAM_SL: rv.u = vtl->stop_length; break;
730 case PARAM_DL: rv.b = vtl->drawlines; break;
731 case PARAM_LT: rv.u = vtl->line_thickness; break;
732 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
735 /* Convert to store internally
736 NB file operation always in internal units (metres per second) */
737 vik_units_speed_t speed_units = a_vik_get_units_speed ();
738 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
739 rv.d = vtl->velocity_min;
740 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
741 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
742 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
743 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
746 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
751 /* Convert to store internally
752 NB file operation always in internal units (metres per second) */
753 vik_units_speed_t speed_units = a_vik_get_units_speed ();
754 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
755 rv.d = vtl->velocity_max;
756 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
757 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
758 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
759 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
762 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
765 case PARAM_DLA: rv.b = vtl->drawlabels; break;
766 case PARAM_DI: rv.b = vtl->drawimages; break;
767 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
768 case PARAM_IS: rv.u = vtl->image_size; break;
769 case PARAM_IA: rv.u = vtl->image_alpha; break;
770 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
771 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
772 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
773 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
774 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
775 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
776 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
777 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
782 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
793 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
794 a_gpx_write_file(vtl, f);
795 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
798 g_file_get_contents(tmpname, &dd, &dl, NULL);
799 *len = sizeof(pl) + pl + dl;
800 *data = g_malloc(*len);
801 memcpy(*data, &pl, sizeof(pl));
802 memcpy(*data + sizeof(pl), pd, pl);
803 memcpy(*data + sizeof(pl) + pl, dd, dl);
812 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
814 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
820 memcpy(&pl, data, sizeof(pl));
822 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
825 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
826 g_critical("couldn't open temp file");
829 fwrite(data, len - pl - sizeof(pl), 1, f);
831 a_gpx_read_file(rv, f);
839 static GList * str_array_to_glist(gchar* data[])
843 for (p = (gpointer)data; *p; p++)
844 gl = g_list_prepend(gl, *p);
845 return(g_list_reverse(gl));
848 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
850 return (strcasecmp(s1, s2) == 0);
853 static guint strcase_hash(gconstpointer v)
855 /* 31 bit hash function */
858 gchar s[128]; /* malloc is too slow for reading big files */
861 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
862 p[i] = toupper(t[i]);
868 for (p += 1; *p != '\0'; p++)
869 h = (h << 5) - h + *p;
875 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
877 if (trw_layer_params[PARAM_DM].widget_data == NULL)
878 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
879 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
880 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
882 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
883 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
885 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
886 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
887 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
888 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
890 /* TODO: constants at top */
891 rv->waypoints_visible = rv->tracks_visible = TRUE;
892 rv->drawmode = drawmode;
893 rv->drawpoints = TRUE;
894 rv->drawstops = FALSE;
895 rv->drawelevation = FALSE;
896 rv->elevation_factor = 30;
897 rv->stop_length = 60;
898 rv->drawlines = TRUE;
899 rv->wplabellayout = NULL;
900 rv->wp_right_click_menu = NULL;
901 rv->waypoint_gc = NULL;
902 rv->waypoint_text_gc = NULL;
903 rv->waypoint_bg_gc = NULL;
905 rv->velocity_max = 5.0;
906 rv->velocity_min = 0.0;
907 rv->line_thickness = 1;
908 rv->bg_line_thickness = 0;
909 rv->current_wp = NULL;
910 rv->current_wp_name = NULL;
911 rv->current_track = NULL;
912 rv->current_tpl = NULL;
913 rv->current_tp_track_name = NULL;
914 rv->moving_tp = FALSE;
915 rv->moving_wp = FALSE;
917 rv->ct_sync_done = TRUE;
919 rv->magic_scissors_started = FALSE;
920 rv->magic_scissors_check_added_track = FALSE;
921 rv->magic_scissors_added_track_name = NULL;
922 rv->magic_scissors_current_track = NULL;
923 rv->magic_scissors_append = FALSE;
925 rv->waypoint_rightclick = FALSE;
927 rv->last_tp_track_name = NULL;
929 rv->image_cache = g_queue_new();
931 rv->image_alpha = 255;
932 rv->image_cache_size = 300;
933 rv->drawimages = TRUE;
934 rv->drawlabels = TRUE;
939 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
941 g_hash_table_destroy(trwlayer->waypoints);
942 g_hash_table_destroy(trwlayer->tracks);
944 /* ODC: replace with GArray */
945 trw_layer_free_track_gcs ( trwlayer );
947 if ( trwlayer->wp_right_click_menu )
948 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
950 if ( trwlayer->wplabellayout != NULL)
951 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
953 if ( trwlayer->waypoint_gc != NULL )
954 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
956 if ( trwlayer->waypoint_text_gc != NULL )
957 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
959 if ( trwlayer->waypoint_bg_gc != NULL )
960 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
962 if ( trwlayer->waypoint_font != NULL )
963 gdk_font_unref ( trwlayer->waypoint_font );
965 if ( trwlayer->tpwin != NULL )
966 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
968 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
969 g_queue_free ( trwlayer->image_cache );
972 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
975 dp->xmpp = vik_viewport_get_xmpp ( vp );
976 dp->ympp = vik_viewport_get_ympp ( vp );
977 dp->width = vik_viewport_get_width ( vp );
978 dp->height = vik_viewport_get_height ( vp );
979 dp->center = vik_viewport_get_center ( vp );
980 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
981 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
986 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
987 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
988 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
990 dp->ce1 = dp->center->east_west-w2;
991 dp->ce2 = dp->center->east_west+w2;
992 dp->cn1 = dp->center->north_south-h2;
993 dp->cn2 = dp->center->north_south+h2;
994 } else if ( dp->lat_lon ) {
995 VikCoord upperleft, bottomright;
996 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
997 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
998 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
999 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1000 dp->ce1 = upperleft.east_west;
1001 dp->ce2 = bottomright.east_west;
1002 dp->cn1 = bottomright.north_south;
1003 dp->cn2 = upperleft.north_south;
1006 dp->track_gc_iter = 0;
1009 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1011 static gdouble rv = 0;
1012 if ( tp1->has_timestamp && tp2->has_timestamp )
1014 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1015 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1018 return VIK_TRW_LAYER_TRACK_GC_MIN;
1019 else if ( vtl->velocity_min >= vtl->velocity_max )
1020 return VIK_TRW_LAYER_TRACK_GC_MAX;
1022 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1024 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1025 return VIK_TRW_LAYER_TRACK_GC_MAX;
1029 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1032 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1034 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1035 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1036 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1037 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1040 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1042 /* TODO: this function is a mess, get rid of any redundancy */
1043 GList *list = track->trackpoints;
1045 gboolean useoldvals = TRUE;
1047 gboolean drawpoints;
1049 gboolean drawelevation;
1050 gdouble min_alt, max_alt, alt_diff = 0;
1052 const guint8 tp_size_reg = 2;
1053 const guint8 tp_size_cur = 4;
1056 if ( dp->vtl->drawelevation )
1058 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1059 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1060 alt_diff = max_alt - min_alt;
1063 if ( ! track->visible )
1066 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1067 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1068 trw_layer_draw_track ( name, track, dp, TRUE );
1070 if ( drawing_white_background )
1071 drawpoints = drawstops = FALSE;
1073 drawpoints = dp->vtl->drawpoints;
1074 drawstops = dp->vtl->drawstops;
1077 /* Current track - used for creation */
1078 if ( track == dp->vtl->current_track )
1079 main_gc = dp->vtl->current_track_gc;
1081 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1082 /* Draw all tracks of the layer in special colour */
1083 /* if track is member of selected layer or is the current selected track
1084 then draw in the highlight colour.
1085 NB this supercedes the drawmode */
1086 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1087 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1088 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1089 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1092 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1093 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1095 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1099 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1100 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1102 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1107 int x, y, oldx, oldy;
1108 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1110 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1112 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1114 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1116 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1117 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1123 while ((list = g_list_next(list)))
1125 tp = VIK_TRACKPOINT(list->data);
1126 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1128 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1129 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1130 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1131 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1132 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1134 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1136 if ( drawpoints && ! drawing_white_background )
1139 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1142 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1143 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 );
1146 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 );
1149 if ((!tp->newsegment) && (dp->vtl->drawlines))
1151 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1153 /* UTM only: zone check */
1154 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1155 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1157 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1158 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1159 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1163 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1165 if ( drawing_white_background ) {
1166 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1170 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1171 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1173 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1174 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1178 tmp[1].y = oldy-FIXALTITUDE(list->data);
1180 tmp[2].y = y-FIXALTITUDE(list->next->data);
1185 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1186 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1188 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1189 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1191 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1201 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1203 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1204 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1206 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1207 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1208 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1209 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1212 if ( drawing_white_background )
1213 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1215 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1219 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1220 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1227 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1228 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1229 dp->track_gc_iter = 0;
1232 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1233 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1235 trw_layer_draw_track ( name, track, dp, FALSE );
1238 static void cached_pixbuf_free ( CachedPixbuf *cp )
1240 g_object_unref ( G_OBJECT(cp->pixbuf) );
1241 g_free ( cp->image );
1244 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1246 return strcmp ( cp->image, name );
1249 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1252 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1253 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1254 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1257 GdkPixbuf *sym = NULL;
1258 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1260 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1262 if ( wp->image && dp->vtl->drawimages )
1264 GdkPixbuf *pixbuf = NULL;
1267 if ( dp->vtl->image_alpha == 0)
1270 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1272 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1275 gchar *image = wp->image;
1276 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1277 if ( ! regularthumb )
1279 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1280 image = "\x12\x00"; /* this shouldn't occur naturally. */
1284 CachedPixbuf *cp = NULL;
1285 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1286 if ( dp->vtl->image_size == 128 )
1287 cp->pixbuf = regularthumb;
1290 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1291 g_assert ( cp->pixbuf );
1292 g_object_unref ( G_OBJECT(regularthumb) );
1294 cp->image = g_strdup ( image );
1296 /* needed so 'click picture' tool knows how big the pic is; we don't
1297 * store it in cp because they may have been freed already. */
1298 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1299 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1301 g_queue_push_head ( dp->vtl->image_cache, cp );
1302 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1303 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1305 pixbuf = cp->pixbuf;
1309 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1315 w = gdk_pixbuf_get_width ( pixbuf );
1316 h = gdk_pixbuf_get_height ( pixbuf );
1318 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1320 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1321 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1322 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1323 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1324 // Highlighted - so draw a little border around the chosen one
1325 // single line seems a little weak so draw 2 of them
1326 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1327 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1328 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1329 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1332 if ( dp->vtl->image_alpha == 255 )
1333 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1335 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1337 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1341 /* DRAW ACTUAL DOT */
1342 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1343 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 );
1345 else if ( wp == dp->vtl->current_wp ) {
1346 switch ( dp->vtl->wp_symbol ) {
1347 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;
1348 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;
1349 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;
1350 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 );
1351 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 );
1355 switch ( dp->vtl->wp_symbol ) {
1356 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;
1357 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;
1358 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;
1359 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 );
1360 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;
1364 if ( dp->vtl->drawlabels )
1366 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1367 gint label_x, label_y;
1369 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1370 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1371 label_x = x - width/2;
1373 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1375 label_y = y - dp->vtl->wp_size - height - 2;
1377 /* if highlight mode on, then draw background text in highlight colour */
1378 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1379 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1380 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1381 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1382 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1384 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1387 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1389 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1394 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1396 static struct DrawingParams dp;
1397 g_assert ( l != NULL );
1399 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1402 if ( l->tracks_visible )
1403 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1405 if (l->waypoints_visible)
1406 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1409 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1412 if ( vtl->track_bg_gc )
1414 g_object_unref ( vtl->track_bg_gc );
1415 vtl->track_bg_gc = NULL;
1417 if ( vtl->current_track_gc )
1419 g_object_unref ( vtl->current_track_gc );
1420 vtl->current_track_gc = NULL;
1423 if ( ! vtl->track_gc )
1425 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1426 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1427 g_array_free ( vtl->track_gc, TRUE );
1428 vtl->track_gc = NULL;
1431 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1433 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1434 gint width = vtl->line_thickness;
1436 if ( vtl->track_gc )
1437 trw_layer_free_track_gcs ( vtl );
1439 if ( vtl->track_bg_gc )
1440 g_object_unref ( vtl->track_bg_gc );
1441 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1443 if ( vtl->current_track_gc )
1444 g_object_unref ( vtl->current_track_gc );
1445 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1446 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1448 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1450 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1452 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1453 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1454 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1455 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1456 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1457 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1458 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1459 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1460 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1461 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1463 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1465 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1467 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1470 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1472 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1473 PangoFontDescription *pfd;
1474 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1475 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1476 pango_layout_set_font_description (rv->wplabellayout, pfd);
1477 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1478 pango_font_description_free (pfd);
1480 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1482 trw_layer_new_track_gcs ( rv, vp );
1484 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1485 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1486 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1487 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1489 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1491 rv->has_verified_thumbnails = FALSE;
1492 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1494 rv->wp_draw_symbols = TRUE;
1496 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1498 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1503 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1505 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1507 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1508 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 );
1510 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 );
1513 *new_iter = *((GtkTreeIter *) pass_along[1]);
1514 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1516 if ( ! track->visible )
1517 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1520 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1522 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1523 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1524 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 );
1526 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 );
1529 *new_iter = *((GtkTreeIter *) pass_along[1]);
1530 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1532 if ( ! wp->visible )
1533 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1537 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1540 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1542 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1543 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1545 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1547 if ( ! vtl->tracks_visible )
1548 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1550 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1552 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1553 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1555 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1558 if ( ! vtl->waypoints_visible )
1559 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1561 pass_along[0] = &(vtl->waypoints_iter);
1562 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1564 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1568 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1572 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1573 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1574 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1576 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1578 return (t->visible ^= 1);
1582 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1584 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1586 return (t->visible ^= 1);
1594 // Structure to hold multiple track information for a layer
1603 * Build up layer multiple track information via updating the tooltip_tracks structure
1605 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1607 tt->length = tt->length + vik_track_get_length (tr);
1609 // Ensure times are available
1610 if ( tr->trackpoints &&
1611 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1612 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1615 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1616 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1618 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1619 // Hence initialize to the first 'proper' value
1620 if ( tt->start_time == 0 )
1621 tt->start_time = t1;
1622 if ( tt->end_time == 0 )
1625 // Update find the earliest / last times
1626 if ( t1 < tt->start_time )
1627 tt->start_time = t1;
1628 if ( t2 > tt->end_time )
1631 // Keep track of total time
1632 // there maybe gaps within a track (eg segments)
1633 // but this should be generally good enough for a simple indicator
1634 tt->duration = tt->duration + (int)(t2-t1);
1639 * Generate tooltip text for the layer.
1640 * This is relatively complicated as it considers information for
1641 * no tracks, a single track or multiple tracks
1642 * (which may or may not have timing information)
1644 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1655 static gchar tmp_buf[128];
1658 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1660 // Safety check - I think these should always be valid
1661 if ( vtl->tracks && vtl->waypoints ) {
1662 tooltip_tracks tt = { 0.0, 0, 0 };
1663 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1665 GDate* gdate_start = g_date_new ();
1666 g_date_set_time_t (gdate_start, tt.start_time);
1668 GDate* gdate_end = g_date_new ();
1669 g_date_set_time_t (gdate_end, tt.end_time);
1671 if ( g_date_compare (gdate_start, gdate_end) ) {
1672 // Dates differ so print range on separate line
1673 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1674 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1675 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1678 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1679 if ( tt.start_time != 0 )
1680 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1684 if ( tt.length > 0.0 ) {
1685 gdouble len_in_units;
1687 // Setup info dependent on distance units
1688 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1689 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1690 len_in_units = VIK_METERS_TO_MILES(tt.length);
1693 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1694 len_in_units = tt.length/1000.0;
1697 // Timing information if available
1699 if ( tt.duration > 0 ) {
1700 g_snprintf (tbuf1, sizeof(tbuf1),
1701 _(" in %d:%02d hrs:mins"),
1702 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1704 g_snprintf (tbuf2, sizeof(tbuf2),
1705 _("\n%sTotal Length %.1f %s%s"),
1706 tbuf3, len_in_units, tbuf4, tbuf1);
1709 // Put together all the elements to form compact tooltip text
1710 g_snprintf (tmp_buf, sizeof(tmp_buf),
1711 _("Tracks: %d - Waypoints: %d%s"),
1712 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1714 g_date_free (gdate_start);
1715 g_date_free (gdate_end);
1722 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1726 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1727 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1728 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1730 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1732 // Could be a better way of handling strings - but this works...
1733 gchar time_buf1[20];
1734 gchar time_buf2[20];
1735 time_buf1[0] = '\0';
1736 time_buf2[0] = '\0';
1737 static gchar tmp_buf[100];
1738 // Compact info: Short date eg (11/20/99), duration and length
1739 // Hopefully these are the things that are most useful and so promoted into the tooltip
1740 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1741 // %x The preferred date representation for the current locale without the time.
1742 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1743 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1744 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1746 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1749 // Get length and consider the appropriate distance units
1750 gdouble tr_len = vik_track_get_length(tr);
1751 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1752 switch (dist_units) {
1753 case VIK_UNITS_DISTANCE_KILOMETRES:
1754 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1756 case VIK_UNITS_DISTANCE_MILES:
1757 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1766 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1768 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1769 // NB It's OK to return NULL
1779 * General layer selection function, find out which bit is selected and take appropriate action
1781 gboolean vik_trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1785 case VIK_TREEVIEW_TYPE_LAYER:
1787 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1788 /* Mark for redraw */
1793 case VIK_TREEVIEW_TYPE_SUBLAYER:
1797 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1799 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks );
1800 /* Mark for redraw */
1804 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1806 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->tracks, sublayer ) );
1807 /* Mark for redraw */
1811 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1813 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints );
1814 /* Mark for redraw */
1818 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1820 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->waypoints, sublayer ) );
1821 /* Mark for redraw */
1827 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1836 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1841 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1846 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1848 return l->waypoints;
1851 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1853 static VikCoord fixme;
1854 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1855 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1856 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1857 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1858 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1859 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1860 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1861 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1862 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1865 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1868 static VikCoord fixme;
1872 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1873 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1874 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1875 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1876 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1877 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1878 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1879 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1880 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1885 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1887 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1888 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1890 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1891 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1892 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1893 maxmin[0].lat = wpt_maxmin[0].lat;
1896 maxmin[0].lat = trk_maxmin[0].lat;
1898 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1899 maxmin[0].lon = wpt_maxmin[0].lon;
1902 maxmin[0].lon = trk_maxmin[0].lon;
1904 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1905 maxmin[1].lat = wpt_maxmin[1].lat;
1908 maxmin[1].lat = trk_maxmin[1].lat;
1910 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1911 maxmin[1].lon = wpt_maxmin[1].lon;
1914 maxmin[1].lon = trk_maxmin[1].lon;
1918 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1920 /* 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... */
1921 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1922 trw_layer_find_maxmin (vtl, maxmin);
1923 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1927 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1928 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1933 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1936 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1937 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
1939 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1942 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1944 /* First set the center [in case previously viewing from elsewhere] */
1945 /* Then loop through zoom levels until provided positions are in view */
1946 /* This method is not particularly fast - but should work well enough */
1947 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1949 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1950 vik_viewport_set_center_coord ( vvp, &coord );
1952 /* Convert into definite 'smallest' and 'largest' positions */
1953 struct LatLon minmin;
1954 if ( maxmin[0].lat < maxmin[1].lat )
1955 minmin.lat = maxmin[0].lat;
1957 minmin.lat = maxmin[1].lat;
1959 struct LatLon maxmax;
1960 if ( maxmin[0].lon > maxmin[1].lon )
1961 maxmax.lon = maxmin[0].lon;
1963 maxmax.lon = maxmin[1].lon;
1965 /* Never zoom in too far - generally not that useful, as too close ! */
1966 /* Always recalculate the 'best' zoom level */
1968 vik_viewport_set_zoom ( vvp, zoom );
1970 gdouble min_lat, max_lat, min_lon, max_lon;
1971 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1972 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1973 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1974 /* NB I think the logic used in this test to determine if the bounds is within view
1975 fails if track goes across 180 degrees longitude.
1976 Hopefully that situation is not too common...
1977 Mind you viking doesn't really do edge locations to well anyway */
1978 if ( min_lat < minmin.lat &&
1979 max_lat > minmin.lat &&
1980 min_lon < maxmax.lon &&
1981 max_lon > maxmax.lon )
1982 /* Found within zoom level */
1987 vik_viewport_set_zoom ( vvp, zoom );
1991 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
1993 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
1994 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1995 trw_layer_find_maxmin (vtl, maxmin);
1996 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1999 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2004 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2006 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])) ) ) {
2007 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2010 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2013 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2015 GtkWidget *file_selector;
2017 gboolean failed = FALSE;
2018 file_selector = gtk_file_chooser_dialog_new (title,
2020 GTK_FILE_CHOOSER_ACTION_SAVE,
2021 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2022 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2024 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2026 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2028 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2029 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2031 gtk_widget_hide ( file_selector );
2032 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2037 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2039 gtk_widget_hide ( file_selector );
2040 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2045 gtk_widget_destroy ( file_selector );
2047 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2050 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2052 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2055 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2057 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2060 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2062 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX );
2065 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2067 gpointer layer_and_vlp[2];
2068 layer_and_vlp[0] = pass_along[0];
2069 layer_and_vlp[1] = pass_along[1];
2071 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2072 gchar *auto_save_name = g_strdup ( pass_along[3] );
2073 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2074 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2076 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2078 g_free ( auto_save_name );
2081 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2083 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2084 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2085 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2086 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2088 GTK_RESPONSE_REJECT,
2090 GTK_RESPONSE_ACCEPT,
2093 GtkWidget *label, *entry;
2094 label = gtk_label_new(_("Waypoint Name:"));
2095 entry = gtk_entry_new();
2097 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2098 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2099 gtk_widget_show_all ( label );
2100 gtk_widget_show_all ( entry );
2102 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2104 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2107 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2110 for ( i = strlen(upname)-1; i >= 0; i-- )
2111 upname[i] = toupper(upname[i]);
2113 wp = g_hash_table_lookup ( wps, upname );
2116 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2119 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2120 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2121 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 ) );
2128 gtk_widget_destroy ( dia );
2131 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2133 gchar *default_name = highest_wp_number_get(vtl);
2134 VikWaypoint *wp = vik_waypoint_new();
2135 gchar *returned_name;
2137 wp->coord = *def_coord;
2139 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2142 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2143 g_free (default_name);
2146 g_free (default_name);
2147 vik_waypoint_free(wp);
2151 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2154 struct LatLon one_ll, two_ll;
2155 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2157 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2158 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2159 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2160 VikViewport *vvp = vik_window_viewport(vw);
2161 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2162 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2163 vik_coord_to_latlon(&one, &one_ll);
2164 vik_coord_to_latlon(&two, &two_ll);
2165 if (one_ll.lat > two_ll.lat) {
2166 maxmin[0].lat = one_ll.lat;
2167 maxmin[1].lat = two_ll.lat;
2170 maxmin[0].lat = two_ll.lat;
2171 maxmin[1].lat = one_ll.lat;
2173 if (one_ll.lon > two_ll.lon) {
2174 maxmin[0].lon = one_ll.lon;
2175 maxmin[1].lon = two_ll.lon;
2178 maxmin[0].lon = two_ll.lon;
2179 maxmin[1].lon = one_ll.lon;
2181 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2184 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2186 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2187 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2188 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2190 trw_layer_find_maxmin (vtl, maxmin);
2191 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2194 static void trw_layer_new_wp ( gpointer lav[2] )
2196 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2197 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2198 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2199 instead return true if you want to update. */
2200 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 )
2201 vik_layers_panel_emit_update ( vlp );
2204 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2206 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2207 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2209 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2210 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2211 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2212 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2213 vik_layers_panel_emit_update ( vlp );
2217 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2219 /* NB do not care if wp is visible or not */
2220 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2223 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2225 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2226 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2228 /* Only 1 waypoint - jump straight to it */
2229 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2230 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2231 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2233 /* If at least 2 waypoints - find center and then zoom to fit */
2234 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2236 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2237 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2238 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2241 vik_layers_panel_emit_update ( vlp );
2244 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2246 static gpointer pass_along[2];
2248 GtkWidget *export_submenu;
2249 GtkWidget *wikipedia_submenu;
2250 pass_along[0] = vtl;
2251 pass_along[1] = vlp;
2253 item = gtk_menu_item_new();
2254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2255 gtk_widget_show ( item );
2257 item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") );
2258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2259 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2260 gtk_widget_show ( item );
2262 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2264 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2265 gtk_widget_show ( item );
2267 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2268 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2269 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2270 gtk_widget_show ( item );
2272 item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2273 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2274 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2275 gtk_widget_show ( item );
2277 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint") );
2278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2279 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2280 gtk_widget_show ( item );
2282 export_submenu = gtk_menu_new ();
2283 item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
2284 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2285 gtk_widget_show ( item );
2286 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2288 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point") );
2289 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2290 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2291 gtk_widget_show ( item );
2293 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper") );
2294 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2295 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2296 gtk_widget_show ( item );
2298 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX") );
2299 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2300 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2301 gtk_widget_show ( item );
2303 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
2304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2305 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2306 gtk_widget_show ( item );
2308 #ifdef VIK_CONFIG_GEONAMES
2309 wikipedia_submenu = gtk_menu_new();
2310 item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2311 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2312 gtk_widget_show(item);
2313 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2315 item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2316 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2317 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2318 gtk_widget_show ( item );
2320 item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
2321 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2322 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2323 gtk_widget_show ( item );
2326 #ifdef VIK_CONFIG_OPENSTREETMAP
2327 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
2328 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2329 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2330 gtk_widget_show ( item );
2333 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2334 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2336 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2337 gtk_widget_show ( item );
2340 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2341 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2343 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2344 gtk_widget_show ( item );
2348 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2350 if ( VIK_LAYER(vtl)->realized )
2352 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2354 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2357 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2358 // Visibility column always needed for waypoints
2359 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2360 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2362 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2364 // Actual setting of visibility dependent on the waypoint
2365 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2366 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2367 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2371 highest_wp_number_add_wp(vtl, name);
2372 g_hash_table_insert ( vtl->waypoints, name, wp );
2376 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2378 if ( VIK_LAYER(vtl)->realized )
2380 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2382 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2385 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2386 // Visibility column always needed for tracks
2387 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2388 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2390 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2392 // Actual setting of visibility dependent on the track
2393 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2394 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2395 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2399 g_hash_table_insert ( vtl->tracks, name, t );
2403 /* to be called whenever a track has been deleted or may have been changed. */
2404 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2406 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2407 trw_layer_cancel_current_tp ( vtl, FALSE );
2408 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2409 trw_layer_cancel_last_tp ( vtl );
2412 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2415 gchar *newname = g_strdup(name);
2416 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2417 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2418 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2420 newname = new_newname;
2426 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2428 vik_trw_layer_add_waypoint ( vtl,
2429 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2432 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2434 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
2435 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2436 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2437 vik_track_free ( tr );
2438 vtl->magic_scissors_append = FALSE; /* this means we have added it */
2440 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2441 vik_trw_layer_add_track ( vtl, new_name, tr );
2443 if ( vtl->magic_scissors_check_added_track ) {
2444 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2445 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2446 g_free ( vtl->magic_scissors_added_track_name );
2447 vtl->magic_scissors_added_track_name = g_strdup(new_name);
2452 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2454 *l = g_list_append(*l, (gpointer)name);
2457 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2459 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2460 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2462 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2463 vik_trw_layer_delete_track(vtl_src, name);
2464 vik_trw_layer_add_track(vtl_dest, newname, t);
2466 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2468 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2469 vik_trw_layer_delete_waypoint(vtl_src, name);
2470 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2474 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2476 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2477 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2479 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2480 GList *items = NULL;
2483 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2484 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2486 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2487 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2492 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2493 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2495 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2502 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2503 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2508 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2510 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2511 gboolean was_visible = FALSE;
2515 was_visible = t->visible;
2516 if ( t == vtl->current_track )
2517 vtl->current_track = NULL;
2518 if ( t == vtl->magic_scissors_current_track )
2519 vtl->magic_scissors_current_track = NULL;
2521 /* could be current_tp, so we have to check */
2522 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2524 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2525 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2526 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2528 /* do this last because trk_name may be pointing to actual orig key */
2529 g_hash_table_remove ( vtl->tracks, trk_name );
2534 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2536 gboolean was_visible = FALSE;
2539 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2543 if ( wp == vtl->current_wp ) {
2544 vtl->current_wp = NULL;
2545 vtl->current_wp_name = NULL;
2546 vtl->moving_wp = FALSE;
2549 was_visible = wp->visible;
2550 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2551 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2552 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2554 highest_wp_number_remove_wp(vtl, wp_name);
2555 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2561 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2563 vik_treeview_item_delete (vt, it );
2566 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2569 vtl->current_track = NULL;
2570 vtl->magic_scissors_current_track = NULL;
2571 if (vtl->current_tp_track_name)
2572 trw_layer_cancel_current_tp(vtl, FALSE);
2573 if (vtl->last_tp_track_name)
2574 trw_layer_cancel_last_tp ( vtl );
2576 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2577 g_hash_table_remove_all(vtl->tracks_iters);
2578 g_hash_table_remove_all(vtl->tracks);
2580 /* TODO: only update if the layer is visible (ticked) */
2581 vik_layer_emit_update ( VIK_LAYER(vtl) );
2584 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2586 vtl->current_wp = NULL;
2587 vtl->current_wp_name = NULL;
2588 vtl->moving_wp = FALSE;
2590 highest_wp_number_reset(vtl);
2592 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2593 g_hash_table_remove_all(vtl->waypoints_iters);
2594 g_hash_table_remove_all(vtl->waypoints);
2596 /* TODO: only update if the layer is visible (ticked) */
2597 vik_layer_emit_update ( VIK_LAYER(vtl) );
2600 static void trw_layer_delete_item ( gpointer pass_along[6] )
2602 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2603 gboolean was_visible = FALSE;
2604 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2606 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2610 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2613 vik_layer_emit_update ( VIK_LAYER(vtl) );
2617 static void trw_layer_properties_item ( gpointer pass_along[6] )
2619 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2620 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2622 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2625 gboolean updated = FALSE;
2626 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
2628 if ( updated && VIK_LAYER(vtl)->visible )
2629 vik_layer_emit_update ( VIK_LAYER(vtl) );
2634 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2637 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2639 pass_along[1], /* vlp */
2640 pass_along[3], /* track name */
2641 pass_along[5] ); /* vvp */
2647 Parameter 1 -> VikLayersPanel
2648 Parameter 2 -> VikLayer
2649 Parameter 3 -> VikViewport
2651 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2654 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2655 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2658 /* since vlp not set, vl & vvp should be valid instead! */
2660 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2661 vik_layer_emit_update ( VIK_LAYER(vl) );
2666 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
2668 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2669 if ( trps && trps->data )
2670 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2673 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
2675 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2676 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2677 if ( trps && *trps )
2679 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2681 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2682 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2683 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2684 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2685 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
2689 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2691 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2692 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2694 vtl->current_track = track;
2695 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2697 if ( track->trackpoints )
2698 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2702 * extend a track using magic scissors
2704 static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2706 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2707 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2708 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2710 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2711 vtl->magic_scissors_coord = last_coord;
2712 vtl->magic_scissors_current_track = track;
2713 vtl->magic_scissors_started = TRUE;
2715 if ( track->trackpoints )
2716 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
2720 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2722 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2723 /* Also warn if overwrite old elevation data */
2724 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2726 vik_track_apply_dem_data ( track );
2729 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2731 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2734 trps = g_list_last(trps);
2735 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2738 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
2740 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2743 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2746 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
2748 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2751 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2754 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
2756 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2759 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2763 * Automatically change the viewport to center on the track and zoom to see the extent of the track
2765 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2767 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2768 if ( trps && *trps )
2770 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2771 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2772 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
2773 if ( pass_along[1] )
2774 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2776 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
2780 /*************************************
2781 * merge/split by time routines
2782 *************************************/
2784 /* called for each key in track hash table.
2785 * If the current track has time stamp, add it to the result,
2786 * except the one pointed by "exclude".
2787 * set exclude to NULL if there is no exclude to check.
2788 * Not that result is in reverse (for performance reason).
2794 static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2796 twt_udata *user_data = udata;
2797 VikTrackpoint *p1, *p2;
2799 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2803 if (VIK_TRACK(value)->trackpoints) {
2804 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2805 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2807 if (!p1->has_timestamp || !p2->has_timestamp) {
2808 g_print("no timestamp\n");
2814 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2817 /* called for each key in track hash table. if original track user_data[1] is close enough
2818 * to the passed one, add it to list in user_data[0]
2820 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2823 VikTrackpoint *p1, *p2;
2825 GList **nearby_tracks = ((gpointer *)user_data)[0];
2826 GList *orig_track = ((gpointer *)user_data)[1];
2827 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
2830 * detect reasons for not merging, and return
2831 * if no reason is found not to merge, then do it.
2834 if (VIK_TRACK(value)->trackpoints == orig_track) {
2838 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2839 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2841 if (VIK_TRACK(value)->trackpoints) {
2842 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2843 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2845 if (!p1->has_timestamp || !p2->has_timestamp) {
2846 g_print("no timestamp\n");
2850 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2851 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2853 abs(p1->timestamp - t2) < thr*60)
2860 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2863 /* comparison function used to sort tracks; a and b are hash table keys */
2864 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2866 GHashTable *tracks = user_data;
2869 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2870 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2872 if (t1 < t2) return -1;
2873 if (t1 > t2) return 1;
2877 /* comparison function used to sort trackpoints */
2878 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2880 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2882 if (t1 < t2) return -1;
2883 if (t1 > t2) return 1;
2887 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2889 * comparison function which can be used to sort tracks or waypoints by name
2891 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
2893 const gchar* namea = (const gchar*) a;
2894 const gchar* nameb = (const gchar*) b;
2895 if ( namea == NULL || nameb == NULL)
2898 // Same sort method as used in the vik_treeview_*_alphabetize functions
2899 return strcmp ( namea, nameb );
2903 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2905 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2906 gchar *orig_track_name = pass_along[3];
2907 GList *tracks_with_timestamp = NULL;
2908 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2910 if (track->trackpoints &&
2911 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2912 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2919 udata.result = &tracks_with_timestamp;
2920 udata.exclude = track->trackpoints;
2921 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2922 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2925 if (!tracks_with_timestamp) {
2926 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2930 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2931 // Sort alphabetically for user presentation
2932 tracks_with_timestamp = g_list_sort_with_data (tracks_with_timestamp, sort_alphabetically, NULL);
2935 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2936 tracks_with_timestamp, TRUE,
2937 _("Merge with..."), _("Select track to merge with"));
2938 g_list_free(tracks_with_timestamp);
2943 for (l = merge_list; l != NULL; l = g_list_next(l)) {
2944 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2946 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2947 merge_track->trackpoints = NULL;
2948 vik_trw_layer_delete_track(vtl, l->data);
2949 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2952 /* TODO: free data before free merge_list */
2953 for (l = merge_list; l != NULL; l = g_list_next(l))
2955 g_list_free(merge_list);
2956 vik_layer_emit_update( VIK_LAYER(vtl) );
2960 /* merge by time routine */
2961 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2963 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2964 gchar *orig_track_name = strdup(pass_along[3]);
2967 GList *nearby_tracks;
2970 static guint thr = 1;
2971 guint track_count = 0;
2973 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2974 _("Merge Threshold..."),
2975 _("Merge when time between tracks less than:"),
2977 free(orig_track_name);
2981 /* merge tracks until we can't */
2982 nearby_tracks = NULL;
2986 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2987 trps = track->trackpoints;
2992 if (nearby_tracks) {
2993 g_list_free(nearby_tracks);
2994 nearby_tracks = NULL;
2997 t1 = ((VikTrackpoint *)trps->data)->timestamp;
2998 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3000 /* g_print("Original track times: %d and %d\n", t1, t2); */
3001 params[0] = &nearby_tracks;
3003 params[2] = GUINT_TO_POINTER (thr);
3005 /* get a list of adjacent-in-time tracks */
3006 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3008 /* add original track */
3009 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3013 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3014 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3015 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3016 GList *l = nearby_tracks;
3017 VikTrack *tr = vik_track_new();
3018 tr->visible = track->visible;
3023 t1 = get_first_trackpoint(l)->timestamp;
3024 t2 = get_last_trackpoint(l)->timestamp;
3025 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3029 /* remove trackpoints from merged track, delete track */
3030 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3031 get_track(l)->trackpoints = NULL;
3032 vik_trw_layer_delete_track(vtl, l->data);
3037 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3038 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3040 #undef get_first_trackpoint
3041 #undef get_last_trackpoint
3044 } while (track_count > 1);
3045 g_list_free(nearby_tracks);
3046 free(orig_track_name);
3047 vik_layer_emit_update( VIK_LAYER(vtl) );
3050 /* split by time routine */
3051 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3053 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3054 GList *trps = track->trackpoints;
3056 GList *newlists = NULL;
3057 GList *newtps = NULL;
3059 static guint thr = 1;
3066 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3067 _("Split Threshold..."),
3068 _("Split when time between trackpoints exceeds:"),
3073 /* iterate through trackpoints, and copy them into new lists without touching original list */
3074 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3078 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3080 g_print("panic: ts < prev_ts: this should never happen!\n");
3083 if (ts - prev_ts > thr*60) {
3084 /* flush accumulated trackpoints into new list */
3085 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
3089 /* accumulate trackpoint copies in newtps, in reverse order */
3090 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3092 iter = g_list_next(iter);
3095 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
3098 /* put lists of trackpoints into tracks */
3105 tr = vik_track_new();
3106 tr->visible = track->visible;
3107 tr->trackpoints = (GList *)(iter->data);
3109 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3110 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3111 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3112 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3114 iter = g_list_next(iter);
3116 g_list_free(newlists);
3117 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3118 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3122 * Split a track by the number of points as specified by the user
3124 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3126 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3127 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3129 // Check valid track
3130 GList *trps = track->trackpoints;
3134 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3135 _("Split Every Nth Point"),
3136 _("Split on every Nth point:"),
3137 250, // Default value as per typical limited track capacity of various GPS devices
3141 // Was a valid number returned?
3147 GList *newlists = NULL;
3148 GList *newtps = NULL;
3153 /* accumulate trackpoint copies in newtps, in reverse order */
3154 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3156 if (count >= points) {
3157 /* flush accumulated trackpoints into new list */
3158 newlists = g_list_append(newlists, g_list_reverse(newtps));
3162 iter = g_list_next(iter);
3165 // If there is a remaining chunk put that into the new split list
3166 // This may well be the whole track if no split points were encountered
3168 newlists = g_list_append(newlists, g_list_reverse(newtps));
3171 /* put lists of trackpoints into tracks */
3174 // Only bother updating if the split results in new tracks
3175 if (g_list_length (newlists) > 1) {
3180 tr = vik_track_new();
3181 tr->visible = track->visible;
3182 tr->trackpoints = (GList *)(iter->data);
3184 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3185 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3187 iter = g_list_next(iter);
3189 // Remove original track and then update the display
3190 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3191 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3193 g_list_free(newlists);
3196 /* end of split/merge routines */
3199 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3201 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3203 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3206 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3208 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3209 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3213 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3215 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3220 if (strcmp(newname, sublayer) == 0 )
3223 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3224 if (g_hash_table_lookup( l->waypoints, newname))
3226 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3231 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3232 g_hash_table_steal ( l->waypoints_iters, sublayer );
3234 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3235 highest_wp_number_remove_wp(l, sublayer);
3236 g_hash_table_remove ( l->waypoints, sublayer );
3238 rv = g_strdup(newname);
3240 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3242 highest_wp_number_add_wp(l, rv);
3243 g_hash_table_insert ( l->waypoints, rv, wp );
3244 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3246 /* it hasn't been updated yet so we pass new name */
3247 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3248 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3251 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3254 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3261 if (strcmp(newname, sublayer) == 0)
3264 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3265 if (g_hash_table_lookup( l->tracks, newname))
3267 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3272 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3273 g_hash_table_steal ( l->tracks, sublayer );
3275 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3276 g_hash_table_steal ( l->tracks_iters, sublayer );
3278 rv = g_strdup(newname);
3280 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3282 g_hash_table_insert ( l->tracks, rv, tr );
3283 g_hash_table_insert ( l->tracks_iters, rv, iter );
3285 /* don't forget about current_tp_track_name, update that too */
3286 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3288 l->current_tp_track_name = rv;
3290 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3292 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3293 l->last_tp_track_name = rv;
3295 g_free ( orig_key );
3297 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3298 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3301 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3307 static gboolean is_valid_geocache_name ( gchar *str )
3309 gint len = strlen ( str );
3310 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]));
3313 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3315 gchar *track_name = (gchar *) pass_along[3];
3316 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3317 a_acquire_set_filter_track ( tr, track_name );
3320 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3322 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3323 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3326 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3328 gchar *track_name = (gchar *) pass_along[3];
3329 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3331 gchar *escaped = uri_escape ( tr->comment );
3332 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3333 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3339 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3340 /* viewpoint is now available instead */
3341 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3343 static GtkTreeIter staticiter;
3344 static gpointer pass_along[6];
3346 gboolean rv = FALSE;
3349 pass_along[1] = vlp;
3350 pass_along[2] = GINT_TO_POINTER (subtype);
3351 pass_along[3] = sublayer;
3353 staticiter = *iter; /* will exist after function has ended */
3354 pass_along[4] = &staticiter;
3356 pass_along[5] = vvp;
3358 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3362 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3363 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3364 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3365 gtk_widget_show ( item );
3367 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3368 VikTrwLayer *vtl = l;
3369 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3370 if (tr && tr->property_dialog)
3371 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3374 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3376 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3377 gtk_widget_show ( item );
3379 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3380 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3381 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3382 gtk_widget_show ( item );
3384 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3386 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3387 gtk_widget_show ( item );
3389 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3391 gboolean separator_created = FALSE;
3393 /* could be a right-click using the tool */
3394 if ( vlp != NULL ) {
3395 item = gtk_menu_item_new ();
3396 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3397 gtk_widget_show ( item );
3399 separator_created = TRUE;
3401 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3402 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3403 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3404 gtk_widget_show ( item );
3407 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3409 if ( !separator_created ) {
3410 item = gtk_menu_item_new ();
3411 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3412 gtk_widget_show ( item );
3415 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3416 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3417 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3418 gtk_widget_show ( item );
3424 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3427 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
3428 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3429 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3430 gtk_widget_show ( item );
3433 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3435 GtkWidget *goto_submenu;
3436 item = gtk_menu_item_new ();
3437 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3438 gtk_widget_show ( item );
3440 goto_submenu = gtk_menu_new ();
3441 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3442 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3443 gtk_widget_show ( item );
3444 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3446 item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
3447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3448 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3449 gtk_widget_show ( item );
3451 item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3452 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3453 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3454 gtk_widget_show ( item );
3456 item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
3457 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3458 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3459 gtk_widget_show ( item );
3461 item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3462 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3463 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3464 gtk_widget_show ( item );
3466 item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3468 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3469 gtk_widget_show ( item );
3471 item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3473 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3474 gtk_widget_show ( item );
3476 item = gtk_menu_item_new_with_mnemonic ( _("_View Track") );
3477 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3478 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3479 gtk_widget_show ( item );
3481 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time") );
3482 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3483 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3484 gtk_widget_show ( item );
3486 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3487 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3488 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3489 gtk_widget_show ( item );
3491 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time") );
3492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3493 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3494 gtk_widget_show ( item );
3496 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points") );
3497 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
3498 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3499 gtk_widget_show ( item );
3501 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
3503 item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3505 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3506 gtk_widget_show ( item );
3509 item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3511 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3512 gtk_widget_show ( item );
3514 item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX") );
3515 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3516 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3517 gtk_widget_show ( item );
3519 item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3520 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3521 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3522 gtk_widget_show ( item );
3524 item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Magic Scissors") );
3525 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
3526 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3527 gtk_widget_show ( item );
3529 #ifdef VIK_CONFIG_OPENSTREETMAP
3530 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
3531 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3532 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3533 gtk_widget_show ( item );
3536 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3538 item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3539 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3540 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3541 gtk_widget_show ( item );
3544 item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
3545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3546 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3547 gtk_widget_show ( item );
3549 /* ATM This function is only available via the layers panel, due to needing a vlp */
3551 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3552 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3553 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3555 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3556 gtk_widget_show ( item );
3564 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3567 if (!vtl->current_tpl)
3569 if (!vtl->current_tpl->next)
3572 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3573 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3575 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3578 VikTrackpoint *tp_new = vik_trackpoint_new();
3579 struct LatLon ll_current, ll_next;
3580 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3581 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3583 /* main positional interpolation */
3584 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3585 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3587 /* Now other properties that can be interpolated */
3588 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3590 if (tp_current->has_timestamp && tp_next->has_timestamp) {
3591 /* Note here the division is applied to each part, then added
3592 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3593 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3594 tp_new->has_timestamp = TRUE;
3597 if (tp_current->speed != NAN && tp_next->speed != NAN)
3598 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3600 /* TODO - improve interpolation of course, as it may not be correct.
3601 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3602 [similar applies if value is in radians] */
3603 if (tp_current->course != NAN && tp_next->course != NAN)
3604 tp_new->speed = (tp_current->course + tp_next->course)/2;
3606 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3608 /* Insert new point into the trackpoints list after the current TP */
3609 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3610 gint index = g_list_index ( tr->trackpoints, tp_current );
3612 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3617 /* to be called when last_tpl no long exists. */
3618 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3620 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3621 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3622 vtl->last_tpl = NULL;
3623 vtl->last_tp_track_name = NULL;
3626 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3632 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3636 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3638 if ( vtl->current_tpl )
3640 vtl->current_tpl = NULL;
3641 vtl->current_tp_track_name = NULL;
3642 vik_layer_emit_update(VIK_LAYER(vtl));
3646 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3648 g_assert ( vtl->tpwin != NULL );
3649 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3650 trw_layer_cancel_current_tp ( vtl, TRUE );
3651 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3653 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
3654 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
3656 VikTrack *tr = vik_track_new ();
3657 GList *newglist = g_list_alloc ();
3658 newglist->prev = NULL;
3659 newglist->next = vtl->current_tpl->next;
3660 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3661 tr->trackpoints = newglist;
3663 vtl->current_tpl->next->prev = newglist; /* end old track here */
3664 vtl->current_tpl->next = NULL;
3666 vtl->current_tpl = newglist; /* change tp to first of new track. */
3667 vtl->current_tp_track_name = name;
3669 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3673 vik_trw_layer_add_track ( vtl, name, tr );
3674 vik_layer_emit_update(VIK_LAYER(vtl));
3677 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3679 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3681 g_assert(tr != NULL);
3683 /* can't join with a non-existent trackpoint */
3684 vtl->last_tpl = NULL;
3685 vtl->last_tp_track_name = NULL;
3687 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3689 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3690 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3692 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3694 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3695 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3697 trw_layer_cancel_last_tp ( vtl );
3699 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3700 g_list_free_1 ( vtl->current_tpl );
3701 vtl->current_tpl = new_tpl;
3702 vik_layer_emit_update(VIK_LAYER(vtl));
3706 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3707 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3708 g_list_free_1 ( vtl->current_tpl );
3709 trw_layer_cancel_current_tp ( vtl, FALSE );
3712 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3714 vtl->last_tpl = vtl->current_tpl;
3715 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3716 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3718 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3720 vtl->last_tpl = vtl->current_tpl;
3721 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3722 vik_layer_emit_update(VIK_LAYER(vtl));
3724 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
3726 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
3727 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3729 VikTrack *tr_first = tr1, *tr_last = tr2;
3733 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
3734 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
3735 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
3736 vik_track_reverse ( tr1 );
3737 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
3742 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
3744 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. */
3745 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
3746 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
3747 tr2->trackpoints = NULL;
3749 tmp = vtl->current_tp_track_name;
3751 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
3752 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3754 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
3755 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
3756 vik_trw_layer_delete_track ( vtl, tmp );
3758 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
3759 vik_layer_emit_update(VIK_LAYER(vtl));
3761 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
3763 trw_layer_insert_tp_after_current_tp ( vtl );
3764 vik_layer_emit_update(VIK_LAYER(vtl));
3766 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
3767 vik_layer_emit_update (VIK_LAYER(vtl));
3770 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
3774 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
3775 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
3776 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
3777 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
3778 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
3780 if ( vtl->current_tpl )
3781 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3782 /* set layer name and TP data */
3785 /***************************************************************************
3787 ***************************************************************************/
3789 /*** Utility data structures and functions ****/
3793 gint closest_x, closest_y;
3794 gchar *closest_wp_name;
3795 VikWaypoint *closest_wp;
3801 gint closest_x, closest_y;
3802 gchar *closest_track_name;
3803 VikTrackpoint *closest_tp;
3808 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
3814 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
3816 if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
3817 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
3818 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3820 params->closest_wp_name = name;
3821 params->closest_wp = wp;
3822 params->closest_x = x;
3823 params->closest_y = y;
3827 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
3829 GList *tpl = t->trackpoints;
3838 tp = VIK_TRACKPOINT(tpl->data);
3840 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
3842 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
3843 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
3844 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3846 params->closest_track_name = name;
3847 params->closest_tp = tp;
3848 params->closest_tpl = tpl;
3849 params->closest_x = x;
3850 params->closest_y = y;
3856 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3858 TPSearchParams params;
3862 params.closest_track_name = NULL;
3863 params.closest_tp = NULL;
3864 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3865 return params.closest_tp;
3868 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3870 WPSearchParams params;
3874 params.closest_wp = NULL;
3875 params.closest_wp_name = NULL;
3876 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3877 return params.closest_wp;
3880 /* background drawing hook, to be passed the viewport */
3881 static gboolean tool_sync_done = TRUE;
3883 static gboolean tool_sync(gpointer data)
3885 VikViewport *vvp = data;
3886 gdk_threads_enter();
3887 vik_viewport_sync(vvp);
3888 tool_sync_done = TRUE;
3889 gdk_threads_leave();
3900 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
3903 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
3904 gdk_gc_set_function ( t->gc, GDK_INVERT );
3905 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3906 vik_viewport_sync(t->vvp);
3911 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
3913 VikViewport *vvp = t->vvp;
3914 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3915 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3919 if (tool_sync_done) {
3920 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
3921 tool_sync_done = FALSE;
3925 static void marker_end_move ( tool_ed_t *t )
3927 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3928 g_object_unref ( t->gc );
3932 /*** Edit waypoint ****/
3934 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3936 tool_ed_t *t = g_new(tool_ed_t, 1);
3942 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3944 WPSearchParams params;
3945 tool_ed_t *t = data;
3946 VikViewport *vvp = t->vvp;
3948 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3955 if ( !vtl->vl.visible || !vtl->waypoints_visible )
3958 if ( vtl->current_wp && vtl->current_wp->visible )
3960 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
3962 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
3964 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
3965 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
3967 if ( event->button == 3 )
3968 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3970 marker_begin_move(t, event->x, event->y);
3977 params.x = event->x;
3978 params.y = event->y;
3979 params.closest_wp_name = NULL;
3980 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3981 params.closest_wp = NULL;
3982 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3983 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
3985 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
3986 marker_begin_move(t, event->x, event->y);
3987 g_critical("shouldn't be here");
3990 else if ( params.closest_wp )
3992 if ( event->button == 3 )
3993 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3995 vtl->waypoint_rightclick = FALSE;
3997 vtl->current_wp = params.closest_wp;
3998 vtl->current_wp_name = params.closest_wp_name;
4000 if ( params.closest_wp )
4001 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
4003 /* could make it so don't update if old WP is off screen and new is null but oh well */
4004 vik_layer_emit_update ( VIK_LAYER(vtl) );
4008 vtl->current_wp = NULL;
4009 vtl->current_wp_name = NULL;
4010 vtl->waypoint_rightclick = FALSE;
4011 vik_layer_emit_update ( VIK_LAYER(vtl) );
4015 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4017 tool_ed_t *t = data;
4018 VikViewport *vvp = t->vvp;
4020 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4025 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4028 if ( event->state & GDK_CONTROL_MASK )
4030 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4032 new_coord = tp->coord;
4036 if ( event->state & GDK_SHIFT_MASK )
4038 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4039 if ( wp && wp != vtl->current_wp )
4040 new_coord = wp->coord;
4045 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4047 marker_moveto ( t, x, y );
4054 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4056 tool_ed_t *t = data;
4057 VikViewport *vvp = t->vvp;
4059 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4062 if ( t->holding && event->button == 1 )
4065 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4068 if ( event->state & GDK_CONTROL_MASK )
4070 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4072 new_coord = tp->coord;
4076 if ( event->state & GDK_SHIFT_MASK )
4078 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4079 if ( wp && wp != vtl->current_wp )
4080 new_coord = wp->coord;
4083 marker_end_move ( t );
4085 vtl->current_wp->coord = new_coord;
4086 vik_layer_emit_update ( VIK_LAYER(vtl) );
4089 /* PUT IN RIGHT PLACE!!! */
4090 if ( event->button == 3 && vtl->waypoint_rightclick )
4092 if ( vtl->wp_right_click_menu )
4093 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
4094 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4095 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 );
4096 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4097 vtl->waypoint_rightclick = FALSE;
4102 /**** Begin track ***/
4103 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4108 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4110 vtl->current_track = NULL;
4111 return tool_new_track_click ( vtl, event, vvp );
4114 /*** New track ****/
4116 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4125 } new_track_move_passalong_t;
4127 /* sync and undraw, but only when we have time */
4128 static gboolean ct_sync ( gpointer passalong )
4130 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4131 vik_viewport_sync ( p->vvp );
4132 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4133 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4134 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4135 p->vtl->ct_sync_done = TRUE;
4140 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
4142 /* if we haven't sync'ed yet, we don't have time to do more. */
4143 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
4144 GList *iter = vtl->current_track->trackpoints;
4145 new_track_move_passalong_t *passalong;
4148 while ( iter->next )
4150 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
4151 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
4152 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
4153 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
4155 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
4156 passalong->vtl = vtl;
4157 passalong->vvp = vvp;
4160 passalong->x2 = event->x;
4161 passalong->y2 = event->y;
4163 /* this will sync and undraw when we have time to */
4164 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
4165 vtl->ct_sync_done = FALSE;
4166 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
4168 return VIK_LAYER_TOOL_ACK;
4171 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
4173 if ( vtl->current_track && event->keyval == GDK_Escape ) {
4174 vtl->current_track = NULL;
4175 vik_layer_emit_update ( VIK_LAYER(vtl) );
4177 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
4179 if ( vtl->current_track->trackpoints )
4181 GList *last = g_list_last(vtl->current_track->trackpoints);
4182 g_free ( last->data );
4183 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4185 vik_layer_emit_update ( VIK_LAYER(vtl) );
4191 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4195 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4198 if ( event->button == 3 && vtl->current_track )
4201 if ( vtl->current_track->trackpoints )
4203 GList *last = g_list_last(vtl->current_track->trackpoints);
4204 g_free ( last->data );
4205 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4207 vik_layer_emit_update ( VIK_LAYER(vtl) );
4211 if ( event->type == GDK_2BUTTON_PRESS )
4213 /* subtract last (duplicate from double click) tp then end */
4214 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
4216 GList *last = g_list_last(vtl->current_track->trackpoints);
4217 g_free ( last->data );
4218 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4219 /* undo last, then end */
4220 vtl->current_track = NULL;
4222 vik_layer_emit_update ( VIK_LAYER(vtl) );
4226 if ( ! vtl->current_track )
4228 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
4229 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
4231 vtl->current_track = vik_track_new();
4232 vtl->current_track->visible = TRUE;
4233 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
4235 /* incase it was created by begin track */
4236 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
4241 tp = vik_trackpoint_new();
4242 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
4244 /* snap to other TP */
4245 if ( event->state & GDK_CONTROL_MASK )
4247 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4249 tp->coord = other_tp->coord;
4252 tp->newsegment = FALSE;
4253 tp->has_timestamp = FALSE;
4255 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
4257 vtl->ct_x1 = vtl->ct_x2;
4258 vtl->ct_y1 = vtl->ct_y2;
4259 vtl->ct_x2 = event->x;
4260 vtl->ct_y2 = event->y;
4262 vik_layer_emit_update ( VIK_LAYER(vtl) );
4267 /*** New waypoint ****/
4269 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4274 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4277 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4279 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
4280 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
4281 vik_layer_emit_update ( VIK_LAYER(vtl) );
4286 /*** Edit trackpoint ****/
4288 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
4290 tool_ed_t *t = g_new(tool_ed_t, 1);
4296 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4298 tool_ed_t *t = data;
4299 VikViewport *vvp = t->vvp;
4300 TPSearchParams params;
4301 /* OUTDATED DOCUMENTATION:
4302 find 5 pixel range on each side. then put these UTM, and a pointer
4303 to the winning track name (and maybe the winning track itself), and a
4304 pointer to the winning trackpoint, inside an array or struct. pass
4305 this along, do a foreach on the tracks which will do a foreach on the
4308 params.x = event->x;
4309 params.y = event->y;
4310 params.closest_track_name = NULL;
4311 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4312 params.closest_tp = NULL;
4314 if ( event->button != 1 )
4317 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4320 if ( !vtl->vl.visible || !vtl->tracks_visible )
4323 if ( vtl->current_tpl )
4325 /* 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.) */
4326 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
4327 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
4329 g_assert ( current_tr );
4331 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
4333 if ( current_tr->visible &&
4334 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
4335 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
4336 marker_begin_move ( t, event->x, event->y );
4340 vtl->last_tpl = vtl->current_tpl;
4341 vtl->last_tp_track_name = vtl->current_tp_track_name;
4344 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4346 if ( params.closest_tp )
4348 vtl->current_tpl = params.closest_tpl;
4349 vtl->current_tp_track_name = params.closest_track_name;
4350 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
4351 trw_layer_tpwin_init ( vtl );
4352 vik_layer_emit_update ( VIK_LAYER(vtl) );
4356 /* these aren't the droids you're looking for */
4360 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4362 tool_ed_t *t = data;
4363 VikViewport *vvp = t->vvp;
4365 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4371 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4374 if ( event->state & GDK_CONTROL_MASK )
4376 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4377 if ( tp && tp != vtl->current_tpl->data )
4378 new_coord = tp->coord;
4380 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4383 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4384 marker_moveto ( t, x, y );
4392 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4394 tool_ed_t *t = data;
4395 VikViewport *vvp = t->vvp;
4397 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4399 if ( event->button != 1)
4404 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4407 if ( event->state & GDK_CONTROL_MASK )
4409 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4410 if ( tp && tp != vtl->current_tpl->data )
4411 new_coord = tp->coord;
4414 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4416 marker_end_move ( t );
4418 /* diff dist is diff from orig */
4419 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4420 /* can't join with itself! */
4421 trw_layer_cancel_last_tp ( vtl );
4423 vik_layer_emit_update ( VIK_LAYER(vtl) );
4430 /*** Magic Scissors ***/
4431 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
4436 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4439 if ( !vtl ) return FALSE;
4440 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
4441 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
4443 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
4445 vtl->magic_scissors_coord = *new_end;
4447 vik_layer_emit_update ( VIK_LAYER(vtl) );
4448 /* remove last ' to:...' */
4449 if ( vtl->magic_scissors_current_track->comment ) {
4450 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
4451 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
4452 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
4453 last_to - vtl->magic_scissors_current_track->comment - 1);
4454 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4459 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
4460 struct LatLon start, end;
4461 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
4462 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
4465 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
4466 vik_coord_to_latlon ( &(tmp), &end );
4467 vtl->magic_scissors_coord = tmp; /* for continuations */
4469 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
4470 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
4471 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
4473 vtl->magic_scissors_check_added_track = TRUE;
4474 vtl->magic_scissors_started = FALSE;
4477 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
4478 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
4479 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
4480 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
4481 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
4482 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
4485 /* see if anything was done -- a track was added or appended to */
4486 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
4489 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
4492 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
4494 vtl->magic_scissors_current_track = tr;
4496 g_free ( vtl->magic_scissors_added_track_name );
4497 vtl->magic_scissors_added_track_name = NULL;
4498 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
4499 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
4500 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
4501 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4503 vtl->magic_scissors_check_added_track = FALSE;
4504 vtl->magic_scissors_append = FALSE;
4506 vik_layer_emit_update ( VIK_LAYER(vtl) );
4508 vtl->magic_scissors_started = TRUE;
4509 vtl->magic_scissors_coord = tmp;
4510 vtl->magic_scissors_current_track = NULL;
4515 /*** Show picture ****/
4517 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
4522 /* Params are: vvp, event, last match found or NULL */
4523 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
4525 if ( wp->image && wp->visible )
4527 gint x, y, slackx, slacky;
4528 GdkEventButton *event = (GdkEventButton *) params[1];
4530 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
4531 slackx = wp->image_width / 2;
4532 slacky = wp->image_height / 2;
4533 if ( x <= event->x + slackx && x >= event->x - slackx
4534 && y <= event->y + slacky && y >= event->y - slacky )
4536 params[2] = wp->image; /* we've found a match. however continue searching
4537 * since we want to find the last match -- that
4538 * is, the match that was drawn last. */
4543 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4545 gpointer params[3] = { vvp, event, NULL };
4546 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4548 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
4551 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
4553 ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
4556 gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
4557 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
4558 g_free ( quoted_file );
4559 if ( ! g_spawn_command_line_async ( cmd, &err ) )
4561 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch eog to open file.") );
4562 g_error_free ( err );
4565 #endif /* WINDOWS */
4566 return TRUE; /* found a match */
4569 return FALSE; /* go through other layers, searching for a match */
4572 /***************************************************************************
4574 ***************************************************************************/
4580 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
4582 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
4583 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
4586 /* Structure for thumbnail creating data used in the background thread */
4588 VikTrwLayer *vtl; // Layer needed for redrawing
4589 GSList *pics; // Image list
4590 } thumbnail_create_thread_data;
4592 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
4594 guint total = g_slist_length(tctd->pics), done = 0;
4595 while ( tctd->pics )
4597 a_thumbnails_create ( (gchar *) tctd->pics->data );
4598 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
4600 return -1; /* Abort thread */
4602 tctd->pics = tctd->pics->next;
4605 // Redraw to show the thumbnails as they are now created
4606 gdk_threads_enter();
4607 if ( IS_VIK_LAYER(tctd->vtl) )
4608 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) );
4609 gdk_threads_leave();
4614 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
4616 while ( tctd->pics )
4618 g_free ( tctd->pics->data );
4619 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
4624 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
4626 if ( ! vtl->has_verified_thumbnails )
4628 GSList *pics = NULL;
4629 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
4632 gint len = g_slist_length ( pics );
4633 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
4634 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
4637 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4639 (vik_thr_func) create_thumbnails_thread,
4641 (vik_thr_free_func) thumbnail_create_thread_free,
4649 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
4651 return vtl->coord_mode;
4656 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
4658 vik_coord_convert ( &(wp->coord), *dest_mode );
4661 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
4663 vik_track_convert ( tr, *dest_mode );
4666 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
4668 if ( vtl->coord_mode != dest_mode )
4670 vtl->coord_mode = dest_mode;
4671 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
4672 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
4676 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
4678 return g_hash_table_lookup ( vtl->waypoints, name );
4681 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
4683 return g_hash_table_lookup ( vtl->tracks, name );
4686 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
4688 vtl->menu_selection = selection;
4691 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
4693 return(vtl->menu_selection);
4696 /* ----------- Downloading maps along tracks --------------- */
4698 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
4700 /* TODO: calculating based on current size of viewport */
4701 const gdouble w_at_zoom_0_125 = 0.0013;
4702 const gdouble h_at_zoom_0_125 = 0.0011;
4703 gdouble zoom_factor = zoom_level/0.125;
4705 wh->lat = h_at_zoom_0_125 * zoom_factor;
4706 wh->lon = w_at_zoom_0_125 * zoom_factor;
4708 return 0; /* all OK */
4711 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
4713 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
4714 (dist->lat >= ABS(to->north_south - from->north_south)))
4717 VikCoord *coord = g_malloc(sizeof(VikCoord));
4718 coord->mode = VIK_COORD_LATLON;
4720 if (ABS(gradient) < 1) {
4721 if (from->east_west > to->east_west)
4722 coord->east_west = from->east_west - dist->lon;
4724 coord->east_west = from->east_west + dist->lon;
4725 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
4727 if (from->north_south > to->north_south)
4728 coord->north_south = from->north_south - dist->lat;
4730 coord->north_south = from->north_south + dist->lat;
4731 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
4737 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
4739 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
4740 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
4742 VikCoord *next = from;
4744 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
4746 list = g_list_prepend(list, next);
4752 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
4754 typedef struct _Rect {
4759 #define GLRECT(iter) ((Rect *)((iter)->data))
4762 GList *rects_to_download = NULL;
4765 if (get_download_area_width(vvp, zoom_level, &wh))
4768 GList *iter = tr->trackpoints;
4772 gboolean new_map = TRUE;
4773 VikCoord *cur_coord, tl, br;
4776 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
4778 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4779 rect = g_malloc(sizeof(Rect));
4782 rect->center = *cur_coord;
4783 rects_to_download = g_list_prepend(rects_to_download, rect);
4788 gboolean found = FALSE;
4789 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4790 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
4801 GList *fillins = NULL;
4802 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
4803 /* seems that ATM the function get_next_coord works only for LATLON */
4804 if ( cur_coord->mode == VIK_COORD_LATLON ) {
4805 /* fill-ins for far apart points */
4806 GList *cur_rect, *next_rect;
4807 for (cur_rect = rects_to_download;
4808 (next_rect = cur_rect->next) != NULL;
4809 cur_rect = cur_rect->next) {
4810 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
4811 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
4812 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
4816 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
4819 GList *iter = fillins;
4821 cur_coord = (VikCoord *)(iter->data);
4822 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4823 rect = g_malloc(sizeof(Rect));
4826 rect->center = *cur_coord;
4827 rects_to_download = g_list_prepend(rects_to_download, rect);
4832 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4833 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
4837 for (iter = fillins; iter; iter = iter->next)
4839 g_list_free(fillins);
4841 if (rects_to_download) {
4842 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
4843 g_free(rect_iter->data);
4844 g_list_free(rects_to_download);
4848 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
4851 gint selected_map, default_map;
4852 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
4853 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
4854 gint selected_zoom, default_zoom;
4858 VikTrwLayer *vtl = pass_along[0];
4859 VikLayersPanel *vlp = pass_along[1];
4860 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4861 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
4863 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS);
4864 int num_maps = g_list_length(vmls);
4867 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
4871 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
4872 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
4874 gchar **np = map_names;
4875 VikMapsLayer **lp = map_layers;
4876 for (i = 0; i < num_maps; i++) {
4877 gboolean dup = FALSE;
4878 vml = (VikMapsLayer *)(vmls->data);
4879 for (j = 0; j < i; j++) { /* no duplicate allowed */
4880 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
4887 *np++ = vik_maps_layer_get_map_label(vml);
4893 num_maps = lp - map_layers;
4895 for (default_map = 0; default_map < num_maps; default_map++) {
4896 /* TODO: check for parent layer's visibility */
4897 if (VIK_LAYER(map_layers[default_map])->visible)
4900 default_map = (default_map == num_maps) ? 0 : default_map;
4902 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
4903 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
4904 if (cur_zoom == zoom_vals[default_zoom])
4907 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
4909 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
4912 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
4915 for (i = 0; i < num_maps; i++)
4916 g_free(map_names[i]);
4924 /**** lowest waypoint number calculation ***/
4925 static gint highest_wp_number_name_to_number(const gchar *name) {
4926 if ( strlen(name) == 3 ) {
4928 if ( n < 100 && name[0] != '0' )
4930 if ( n < 10 && name[0] != '0' )
4938 static void highest_wp_number_reset(VikTrwLayer *vtl)
4940 vtl->highest_wp_number = -1;
4943 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
4945 /* if is bigger that top, add it */
4946 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
4947 if ( new_wp_num > vtl->highest_wp_number )
4948 vtl->highest_wp_number = new_wp_num;
4951 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
4953 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
4954 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
4955 if ( vtl->highest_wp_number == old_wp_num ) {
4957 vtl->highest_wp_number --;
4959 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4960 /* search down until we find something that *does* exist */
4962 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
4963 vtl->highest_wp_number --;
4964 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4969 /* get lowest unused number */
4970 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
4973 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
4975 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
4976 return g_strdup(buf);