2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2011, Rob Norris <rw_norris@hotmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #define WAYPOINT_FONT "Sans 8"
28 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
29 /* viktrwlayer.c -- 5000+ lines can make a difference in the state of things */
36 #include "vikmapslayer.h"
37 #include "viktrwlayer_tpwin.h"
38 #include "viktrwlayer_propwin.h"
39 #include "garminsymbols.h"
40 #include "thumbnails.h"
41 #include "background.h"
46 #include "geonamessearch.h"
47 #ifdef VIK_CONFIG_OPENSTREETMAP
48 #include "osm-traces.h"
51 #include "datasources.h"
54 #include "icons/icons.h"
68 #include <gdk/gdkkeysyms.h>
70 #include <glib/gstdio.h>
71 #include <glib/gi18n.h>
73 /* Relax some dependencies */
74 #if ! GLIB_CHECK_VERSION(2,12,0)
75 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
76 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
79 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
80 #define VIK_TRW_LAYER_TRACK_GC 13
81 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
82 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
83 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
84 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
86 #define DRAWMODE_BY_TRACK 0
87 #define DRAWMODE_BY_VELOCITY 1
88 #define DRAWMODE_ALL_BLACK 2
93 /* this is how it knows when you click if you are clicking close to a trackpoint. */
94 #define TRACKPOINT_SIZE_APPROX 5
95 #define WAYPOINT_SIZE_APPROX 5
97 #define MIN_STOP_LENGTH 15
98 #define MAX_STOP_LENGTH 86400
99 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
100 /* this is multiplied by user-inputted value from 1-100. */
102 VIK_TRW_LAYER_SUBLAYER_TRACKS,
103 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
104 VIK_TRW_LAYER_SUBLAYER_TRACK,
105 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
108 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
110 struct _VikTrwLayer {
113 GHashTable *tracks_iters;
114 GHashTable *waypoints_iters;
115 GHashTable *waypoints;
116 GtkTreeIter waypoints_iter, tracks_iter;
117 gboolean tracks_visible, waypoints_visible;
120 guint8 drawelevation;
121 guint8 elevation_factor;
125 guint8 line_thickness;
126 guint8 bg_line_thickness;
130 gboolean wp_draw_symbols;
132 gdouble velocity_min, velocity_max;
134 guint16 track_gc_iter;
135 GdkGC *current_track_gc;
138 GdkGC *waypoint_text_gc;
139 GdkGC *waypoint_bg_gc;
140 GdkFont *waypoint_font;
141 VikTrack *current_track;
142 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
143 gboolean ct_sync_done;
146 VikCoordMode coord_mode;
148 /* wp editing tool */
149 VikWaypoint *current_wp;
150 gchar *current_wp_name;
152 gboolean waypoint_rightclick;
154 /* track editing tool */
156 gchar *current_tp_track_name;
157 VikTrwLayerTpwin *tpwin;
159 /* weird hack for joining tracks */
161 gchar *last_tp_track_name;
163 /* track editing tool -- more specifically, moving tps */
166 /* route finder tool */
167 gboolean route_finder_started;
168 VikCoord route_finder_coord;
169 gboolean route_finder_check_added_track;
170 gchar *route_finder_added_track_name;
171 VikTrack *route_finder_current_track;
172 gboolean route_finder_append;
179 guint16 image_cache_size;
181 /* for waypoint text */
182 PangoLayout *wplabellayout;
184 gboolean has_verified_thumbnails;
186 GtkMenu *wp_right_click_menu;
187 GtkMenu *track_right_click_menu;
190 VikStdLayerMenuItem menu_selection;
192 gint highest_wp_number;
195 /* A caached waypoint image. */
198 gchar *image; /* filename */
201 struct DrawingParams {
205 guint16 width, height;
206 const VikCoord *center;
208 gboolean one_zone, lat_lon;
209 gdouble ce1, ce2, cn1, cn2;
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 void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
228 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
229 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
230 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
231 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
232 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
233 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
234 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
235 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
236 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
237 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
238 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
239 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
240 static void trw_layer_show_picture ( 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_delete_all_tracks ( gpointer lav[2] );
250 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
251 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
252 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
253 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
254 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
255 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
256 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
257 #ifdef VIK_CONFIG_GEOCACHES
258 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
262 static void trw_layer_properties_item ( gpointer pass_along[6] );
263 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
264 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
266 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
267 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
268 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
270 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
271 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
272 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
273 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
274 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
276 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
277 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
278 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
279 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
280 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
281 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
282 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
283 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
284 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
285 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
286 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
287 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
288 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
289 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
290 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
291 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
292 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
293 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
294 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
295 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
298 static void cached_pixbuf_free ( CachedPixbuf *cp );
299 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
301 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
302 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
304 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
305 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
306 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
308 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
309 static void highest_wp_number_reset(VikTrwLayer *vtl);
310 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
311 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
314 static VikToolInterface trw_layer_tools[] = {
315 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
316 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
318 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
319 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
320 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
322 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
323 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
325 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
326 (VikToolMouseFunc) tool_edit_waypoint_click,
327 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
328 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
330 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
331 (VikToolMouseFunc) tool_edit_trackpoint_click,
332 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
333 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
335 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
336 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
338 { N_("Route Finder"), (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
339 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
341 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
343 /****** PARAMETERS ******/
345 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
346 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
348 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
349 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
352 static VikLayerParamScale params_scales[] = {
353 /* min max step digits */
354 { 1, 10, 1, 0 }, /* line_thickness */
355 { 0.0, 99.0, 1, 2 }, /* velocity_min */
356 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
357 /* 5 * step == how much to turn */
358 { 16, 128, 3.2, 0 }, /* image_size */
359 { 0, 255, 5, 0 }, /* image alpha */
360 { 5, 500, 5, 0 }, /* image cache_size */
361 { 0, 8, 1, 0 }, /* image cache_size */
362 { 1, 64, 1, 0 }, /* wpsize */
363 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
364 { 1, 100, 1, 0 }, /* stop_length */
367 VikLayerParam trw_layer_params[] = {
368 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
369 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
371 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
372 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
373 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
374 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
375 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
377 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
378 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
380 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
381 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
382 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
383 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
384 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
386 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
387 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
388 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
389 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
390 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
391 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
392 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
393 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
395 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
396 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
397 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
398 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
401 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 };
404 *** 1) Add to trw_layer_params and enumeration
405 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
408 /****** END PARAMETERS ******/
410 static VikTrwLayer* trw_layer_new ( gint drawmode );
411 /* Layer Interface function definitions */
412 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
413 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
414 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
415 static void trw_layer_free ( VikTrwLayer *trwlayer );
416 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
417 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
418 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
419 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
420 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
421 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
422 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
423 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
424 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
425 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
426 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
427 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
428 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
429 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
430 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
431 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
432 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
433 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
434 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
435 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
436 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
437 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
438 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
439 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
440 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
441 /* End Layer Interface function definitions */
443 VikLayerInterface vik_trw_layer_interface = {
448 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
452 params_groups, /* params_groups */
453 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
457 (VikLayerFuncCreate) trw_layer_create,
458 (VikLayerFuncRealize) trw_layer_realize,
459 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
460 (VikLayerFuncFree) trw_layer_free,
462 (VikLayerFuncProperties) NULL,
463 (VikLayerFuncDraw) trw_layer_draw,
464 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
466 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
467 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
469 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
470 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
472 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
473 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
474 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
475 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
476 (VikLayerFuncLayerSelected) trw_layer_selected,
478 (VikLayerFuncMarshall) trw_layer_marshall,
479 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
481 (VikLayerFuncSetParam) trw_layer_set_param,
482 (VikLayerFuncGetParam) trw_layer_get_param,
484 (VikLayerFuncReadFileData) a_gpspoint_read_file,
485 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
487 (VikLayerFuncDeleteItem) trw_layer_del_item,
488 (VikLayerFuncCutItem) trw_layer_cut_item,
489 (VikLayerFuncCopyItem) trw_layer_copy_item,
490 (VikLayerFuncPasteItem) trw_layer_paste_item,
491 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
493 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
495 (VikLayerFuncSelectClick) trw_layer_select_click,
496 (VikLayerFuncSelectMove) trw_layer_select_move,
497 (VikLayerFuncSelectRelease) trw_layer_select_release,
498 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
501 /* for copy & paste (I think?) */
509 GType vik_trw_layer_get_type ()
511 static GType vtl_type = 0;
515 static const GTypeInfo vtl_info =
517 sizeof (VikTrwLayerClass),
518 NULL, /* base_init */
519 NULL, /* base_finalize */
520 NULL, /* class init */
521 NULL, /* class_finalize */
522 NULL, /* class_data */
523 sizeof (VikTrwLayer),
525 NULL /* instance init */
527 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
533 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
535 static gpointer pass_along[6];
541 pass_along[1] = NULL;
542 pass_along[2] = GINT_TO_POINTER (subtype);
543 pass_along[3] = sublayer;
544 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
545 pass_along[5] = NULL;
547 trw_layer_delete_item ( pass_along );
550 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
552 static gpointer pass_along[6];
558 pass_along[1] = NULL;
559 pass_along[2] = GINT_TO_POINTER (subtype);
560 pass_along[3] = sublayer;
561 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
562 pass_along[5] = NULL;
564 trw_layer_copy_item_cb(pass_along);
565 trw_layer_cut_item_cb(pass_along);
568 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
570 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
571 gint subtype = GPOINTER_TO_INT (pass_along[2]);
572 gpointer * sublayer = pass_along[3];
576 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
579 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
580 subtype, len, (const gchar*) sublayer, data);
584 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
586 trw_layer_copy_item_cb(pass_along);
587 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
588 trw_layer_delete_item(pass_along);
591 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
602 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
604 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
606 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
609 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
610 fi = g_malloc ( *len );
611 fi->len = strlen(sublayer) + 1;
612 memcpy(fi->data, sublayer, fi->len);
613 memcpy(fi->data + fi->len, id, il);
615 *item = (guint8 *)fi;
618 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
620 FlatItem *fi = (FlatItem *) item;
622 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
627 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
628 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
629 vik_trw_layer_add_waypoint ( vtl, name, w );
630 waypoint_convert(name, w, &vtl->coord_mode);
631 // Consider if redraw necessary for the new item
632 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
633 vik_layer_emit_update ( VIK_LAYER(vtl) );
636 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
640 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
641 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
642 vik_trw_layer_add_track ( vtl, name, t );
643 track_convert(name, t, &vtl->coord_mode);
644 // Consider if redraw necessary for the new item
645 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
646 vik_layer_emit_update ( VIK_LAYER(vtl) );
652 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
659 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
663 case PARAM_TV: vtl->tracks_visible = data.b; break;
664 case PARAM_WV: vtl->waypoints_visible = data.b; break;
665 case PARAM_DM: vtl->drawmode = data.u; break;
666 case PARAM_DP: vtl->drawpoints = data.b; break;
667 case PARAM_DE: vtl->drawelevation = data.b; break;
668 case PARAM_DS: vtl->drawstops = data.b; break;
669 case PARAM_DL: vtl->drawlines = data.b; break;
670 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
671 vtl->stop_length = data.u;
673 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
674 vtl->elevation_factor = data.u;
676 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
678 vtl->line_thickness = data.u;
679 trw_layer_new_track_gcs ( vtl, vp );
682 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
684 vtl->bg_line_thickness = data.u;
685 trw_layer_new_track_gcs ( vtl, vp );
690 /* Convert to store internally
691 NB file operation always in internal units (metres per second) */
692 vik_units_speed_t speed_units = a_vik_get_units_speed ();
693 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
694 vtl->velocity_min = data.d;
695 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
696 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
697 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
698 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
701 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
706 /* Convert to store internally
707 NB file operation always in internal units (metres per second) */
708 vik_units_speed_t speed_units = a_vik_get_units_speed ();
709 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
710 vtl->velocity_max = data.d;
711 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
712 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
713 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
714 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
717 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
720 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
721 case PARAM_DLA: vtl->drawlabels = data.b; break;
722 case PARAM_DI: vtl->drawimages = data.b; break;
723 case PARAM_IS: if ( data.u != vtl->image_size )
725 vtl->image_size = data.u;
726 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
727 g_queue_free ( vtl->image_cache );
728 vtl->image_cache = g_queue_new ();
731 case PARAM_IA: vtl->image_alpha = data.u; break;
732 case PARAM_ICS: vtl->image_cache_size = data.u;
733 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
734 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
736 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
737 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
738 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
739 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
740 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
741 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
742 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
747 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
749 VikLayerParamData rv;
752 case PARAM_TV: rv.b = vtl->tracks_visible; break;
753 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
754 case PARAM_DM: rv.u = vtl->drawmode; break;
755 case PARAM_DP: rv.b = vtl->drawpoints; break;
756 case PARAM_DE: rv.b = vtl->drawelevation; break;
757 case PARAM_EF: rv.u = vtl->elevation_factor; break;
758 case PARAM_DS: rv.b = vtl->drawstops; break;
759 case PARAM_SL: rv.u = vtl->stop_length; break;
760 case PARAM_DL: rv.b = vtl->drawlines; break;
761 case PARAM_LT: rv.u = vtl->line_thickness; break;
762 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
765 /* Convert to store internally
766 NB file operation always in internal units (metres per second) */
767 vik_units_speed_t speed_units = a_vik_get_units_speed ();
768 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
769 rv.d = vtl->velocity_min;
770 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
771 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
772 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
773 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
776 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
781 /* Convert to store internally
782 NB file operation always in internal units (metres per second) */
783 vik_units_speed_t speed_units = a_vik_get_units_speed ();
784 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
785 rv.d = vtl->velocity_max;
786 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
787 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
788 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
789 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
792 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
795 case PARAM_DLA: rv.b = vtl->drawlabels; break;
796 case PARAM_DI: rv.b = vtl->drawimages; break;
797 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
798 case PARAM_IS: rv.u = vtl->image_size; break;
799 case PARAM_IA: rv.u = vtl->image_alpha; break;
800 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
801 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
802 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
803 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
804 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
805 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
806 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
807 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
812 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
823 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
824 a_gpx_write_file(vtl, f);
825 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
828 g_file_get_contents(tmpname, &dd, &dl, NULL);
829 *len = sizeof(pl) + pl + dl;
830 *data = g_malloc(*len);
831 memcpy(*data, &pl, sizeof(pl));
832 memcpy(*data + sizeof(pl), pd, pl);
833 memcpy(*data + sizeof(pl) + pl, dd, dl);
842 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
844 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
850 memcpy(&pl, data, sizeof(pl));
852 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
855 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
856 g_critical("couldn't open temp file");
859 fwrite(data, len - pl - sizeof(pl), 1, f);
861 a_gpx_read_file(rv, f);
869 static GList * str_array_to_glist(gchar* data[])
873 for (p = (gpointer)data; *p; p++)
874 gl = g_list_prepend(gl, *p);
875 return(g_list_reverse(gl));
878 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
880 return (strcasecmp(s1, s2) == 0);
883 static guint strcase_hash(gconstpointer v)
885 /* 31 bit hash function */
888 gchar s[128]; /* malloc is too slow for reading big files */
891 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
892 p[i] = toupper(t[i]);
898 for (p += 1; *p != '\0'; p++)
899 h = (h << 5) - h + *p;
905 static VikTrwLayer* trw_layer_new ( gint drawmode )
907 if (trw_layer_params[PARAM_DM].widget_data == NULL)
908 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
909 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
910 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
912 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
913 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
915 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
916 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
917 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
918 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
920 /* TODO: constants at top */
921 rv->waypoints_visible = rv->tracks_visible = TRUE;
922 rv->drawmode = drawmode;
923 rv->drawpoints = TRUE;
924 rv->drawstops = FALSE;
925 rv->drawelevation = FALSE;
926 rv->elevation_factor = 30;
927 rv->stop_length = 60;
928 rv->drawlines = TRUE;
929 rv->wplabellayout = NULL;
930 rv->wp_right_click_menu = NULL;
931 rv->track_right_click_menu = NULL;
932 rv->waypoint_gc = NULL;
933 rv->waypoint_text_gc = NULL;
934 rv->waypoint_bg_gc = NULL;
936 rv->velocity_max = 5.0;
937 rv->velocity_min = 0.0;
938 rv->line_thickness = 1;
939 rv->bg_line_thickness = 0;
940 rv->current_wp = NULL;
941 rv->current_wp_name = NULL;
942 rv->current_track = NULL;
943 rv->current_tpl = NULL;
944 rv->current_tp_track_name = NULL;
945 rv->moving_tp = FALSE;
946 rv->moving_wp = FALSE;
948 rv->ct_sync_done = TRUE;
950 rv->route_finder_started = FALSE;
951 rv->route_finder_check_added_track = FALSE;
952 rv->route_finder_added_track_name = NULL;
953 rv->route_finder_current_track = NULL;
954 rv->route_finder_append = FALSE;
956 rv->waypoint_rightclick = FALSE;
958 rv->last_tp_track_name = NULL;
960 rv->image_cache = g_queue_new();
962 rv->image_alpha = 255;
963 rv->image_cache_size = 300;
964 rv->drawimages = TRUE;
965 rv->drawlabels = TRUE;
970 static void trw_layer_free ( VikTrwLayer *trwlayer )
972 g_hash_table_destroy(trwlayer->waypoints);
973 g_hash_table_destroy(trwlayer->tracks);
975 /* ODC: replace with GArray */
976 trw_layer_free_track_gcs ( trwlayer );
978 if ( trwlayer->wp_right_click_menu )
979 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
981 if ( trwlayer->track_right_click_menu )
982 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
984 if ( trwlayer->wplabellayout != NULL)
985 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
987 if ( trwlayer->waypoint_gc != NULL )
988 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
990 if ( trwlayer->waypoint_text_gc != NULL )
991 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
993 if ( trwlayer->waypoint_bg_gc != NULL )
994 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
996 if ( trwlayer->waypoint_font != NULL )
997 gdk_font_unref ( trwlayer->waypoint_font );
999 if ( trwlayer->tpwin != NULL )
1000 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1002 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1003 g_queue_free ( trwlayer->image_cache );
1006 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
1009 dp->xmpp = vik_viewport_get_xmpp ( vp );
1010 dp->ympp = vik_viewport_get_ympp ( vp );
1011 dp->width = vik_viewport_get_width ( vp );
1012 dp->height = vik_viewport_get_height ( vp );
1013 dp->center = vik_viewport_get_center ( vp );
1014 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1015 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1020 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1021 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1022 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1024 dp->ce1 = dp->center->east_west-w2;
1025 dp->ce2 = dp->center->east_west+w2;
1026 dp->cn1 = dp->center->north_south-h2;
1027 dp->cn2 = dp->center->north_south+h2;
1028 } else if ( dp->lat_lon ) {
1029 VikCoord upperleft, bottomright;
1030 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1031 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1032 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1033 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1034 dp->ce1 = upperleft.east_west;
1035 dp->ce2 = bottomright.east_west;
1036 dp->cn1 = bottomright.north_south;
1037 dp->cn2 = upperleft.north_south;
1040 dp->track_gc_iter = 0;
1043 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1045 static gdouble rv = 0;
1046 if ( tp1->has_timestamp && tp2->has_timestamp )
1048 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1049 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1052 return VIK_TRW_LAYER_TRACK_GC_MIN;
1053 else if ( vtl->velocity_min >= vtl->velocity_max )
1054 return VIK_TRW_LAYER_TRACK_GC_MAX;
1056 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1058 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1059 return VIK_TRW_LAYER_TRACK_GC_MAX;
1063 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1066 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1068 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1069 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1070 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1071 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1074 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1076 /* TODO: this function is a mess, get rid of any redundancy */
1077 GList *list = track->trackpoints;
1079 gboolean useoldvals = TRUE;
1081 gboolean drawpoints;
1083 gboolean drawelevation;
1084 gdouble min_alt, max_alt, alt_diff = 0;
1086 const guint8 tp_size_reg = 2;
1087 const guint8 tp_size_cur = 4;
1090 if ( dp->vtl->drawelevation )
1092 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1093 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1094 alt_diff = max_alt - min_alt;
1097 if ( ! track->visible )
1100 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1101 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1102 trw_layer_draw_track ( name, track, dp, TRUE );
1104 if ( drawing_white_background )
1105 drawpoints = drawstops = FALSE;
1107 drawpoints = dp->vtl->drawpoints;
1108 drawstops = dp->vtl->drawstops;
1111 /* Current track - used for creation */
1112 if ( track == dp->vtl->current_track )
1113 main_gc = dp->vtl->current_track_gc;
1115 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1116 /* Draw all tracks of the layer in special colour */
1117 /* if track is member of selected layer or is the current selected track
1118 then draw in the highlight colour.
1119 NB this supercedes the drawmode */
1120 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1121 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1122 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1123 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1126 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1127 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1129 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1133 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1134 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1136 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1141 int x, y, oldx, oldy;
1142 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1144 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1146 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1148 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1150 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1151 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1157 while ((list = g_list_next(list)))
1159 tp = VIK_TRACKPOINT(list->data);
1160 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1162 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1163 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1164 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1165 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1166 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1168 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1170 if ( drawpoints && ! drawing_white_background )
1173 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1176 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1177 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 );
1180 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 );
1183 if ((!tp->newsegment) && (dp->vtl->drawlines))
1185 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1187 /* UTM only: zone check */
1188 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1189 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1191 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1192 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1193 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1197 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1199 if ( drawing_white_background ) {
1200 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1204 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1205 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1207 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1208 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1212 tmp[1].y = oldy-FIXALTITUDE(list->data);
1214 tmp[2].y = y-FIXALTITUDE(list->next->data);
1219 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1220 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1222 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1223 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1225 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1235 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1237 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1238 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1240 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1241 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1242 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1243 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1246 if ( drawing_white_background )
1247 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1249 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1253 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1254 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1261 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1262 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1263 dp->track_gc_iter = 0;
1266 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1267 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1269 trw_layer_draw_track ( name, track, dp, FALSE );
1272 static void cached_pixbuf_free ( CachedPixbuf *cp )
1274 g_object_unref ( G_OBJECT(cp->pixbuf) );
1275 g_free ( cp->image );
1278 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1280 return strcmp ( cp->image, name );
1283 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1286 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1287 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1288 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1291 GdkPixbuf *sym = NULL;
1292 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1294 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1296 if ( wp->image && dp->vtl->drawimages )
1298 GdkPixbuf *pixbuf = NULL;
1301 if ( dp->vtl->image_alpha == 0)
1304 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1306 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1309 gchar *image = wp->image;
1310 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1311 if ( ! regularthumb )
1313 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1314 image = "\x12\x00"; /* this shouldn't occur naturally. */
1318 CachedPixbuf *cp = NULL;
1319 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1320 if ( dp->vtl->image_size == 128 )
1321 cp->pixbuf = regularthumb;
1324 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1325 g_assert ( cp->pixbuf );
1326 g_object_unref ( G_OBJECT(regularthumb) );
1328 cp->image = g_strdup ( image );
1330 /* needed so 'click picture' tool knows how big the pic is; we don't
1331 * store it in cp because they may have been freed already. */
1332 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1333 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1335 g_queue_push_head ( dp->vtl->image_cache, cp );
1336 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1337 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1339 pixbuf = cp->pixbuf;
1343 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1349 w = gdk_pixbuf_get_width ( pixbuf );
1350 h = gdk_pixbuf_get_height ( pixbuf );
1352 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1354 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1355 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1356 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1357 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1358 // Highlighted - so draw a little border around the chosen one
1359 // single line seems a little weak so draw 2 of them
1360 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1361 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1362 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1363 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1366 if ( dp->vtl->image_alpha == 255 )
1367 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1369 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1371 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1375 /* DRAW ACTUAL DOT */
1376 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1377 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 );
1379 else if ( wp == dp->vtl->current_wp ) {
1380 switch ( dp->vtl->wp_symbol ) {
1381 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;
1382 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;
1383 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;
1384 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 );
1385 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 );
1389 switch ( dp->vtl->wp_symbol ) {
1390 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;
1391 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;
1392 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;
1393 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 );
1394 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;
1398 if ( dp->vtl->drawlabels )
1400 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1401 gint label_x, label_y;
1403 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1404 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1405 label_x = x - width/2;
1407 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1409 label_y = y - dp->vtl->wp_size - height - 2;
1411 /* if highlight mode on, then draw background text in highlight colour */
1412 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1413 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1414 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1415 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1416 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1418 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1421 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1423 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1428 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1430 static struct DrawingParams dp;
1431 g_assert ( l != NULL );
1433 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1436 if ( l->tracks_visible )
1437 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1439 if (l->waypoints_visible)
1440 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1443 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1446 if ( vtl->track_bg_gc )
1448 g_object_unref ( vtl->track_bg_gc );
1449 vtl->track_bg_gc = NULL;
1451 if ( vtl->current_track_gc )
1453 g_object_unref ( vtl->current_track_gc );
1454 vtl->current_track_gc = NULL;
1457 if ( ! vtl->track_gc )
1459 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1460 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1461 g_array_free ( vtl->track_gc, TRUE );
1462 vtl->track_gc = NULL;
1465 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1467 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1468 gint width = vtl->line_thickness;
1470 if ( vtl->track_gc )
1471 trw_layer_free_track_gcs ( vtl );
1473 if ( vtl->track_bg_gc )
1474 g_object_unref ( vtl->track_bg_gc );
1475 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1477 if ( vtl->current_track_gc )
1478 g_object_unref ( vtl->current_track_gc );
1479 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1480 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1482 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1484 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1486 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1487 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1488 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1489 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1490 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1491 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1492 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1493 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1494 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1495 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1497 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1499 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1501 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1504 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1506 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1507 PangoFontDescription *pfd;
1508 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1509 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1510 pango_layout_set_font_description (rv->wplabellayout, pfd);
1511 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1512 pango_font_description_free (pfd);
1514 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1516 trw_layer_new_track_gcs ( rv, vp );
1518 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1519 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1520 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1521 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1523 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1525 rv->has_verified_thumbnails = FALSE;
1526 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1528 rv->wp_draw_symbols = TRUE;
1530 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1532 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1537 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1539 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1541 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1542 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1544 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1547 *new_iter = *((GtkTreeIter *) pass_along[1]);
1548 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1550 if ( ! track->visible )
1551 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1554 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1556 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1557 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1558 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 );
1560 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 );
1563 *new_iter = *((GtkTreeIter *) pass_along[1]);
1564 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1566 if ( ! wp->visible )
1567 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1571 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1574 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1576 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1577 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1579 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1581 if ( ! vtl->tracks_visible )
1582 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1584 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1586 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1587 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1589 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1592 if ( ! vtl->waypoints_visible )
1593 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1595 pass_along[0] = &(vtl->waypoints_iter);
1596 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1598 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1602 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1606 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1607 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1608 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1610 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1612 return (t->visible ^= 1);
1616 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1618 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1620 return (t->visible ^= 1);
1629 * Return a property about tracks for this layer
1631 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1633 return vtl->line_thickness;
1636 // Structure to hold multiple track information for a layer
1645 * Build up layer multiple track information via updating the tooltip_tracks structure
1647 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1649 tt->length = tt->length + vik_track_get_length (tr);
1651 // Ensure times are available
1652 if ( tr->trackpoints &&
1653 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1654 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1657 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1658 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1660 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1661 // Hence initialize to the first 'proper' value
1662 if ( tt->start_time == 0 )
1663 tt->start_time = t1;
1664 if ( tt->end_time == 0 )
1667 // Update find the earliest / last times
1668 if ( t1 < tt->start_time )
1669 tt->start_time = t1;
1670 if ( t2 > tt->end_time )
1673 // Keep track of total time
1674 // there maybe gaps within a track (eg segments)
1675 // but this should be generally good enough for a simple indicator
1676 tt->duration = tt->duration + (int)(t2-t1);
1681 * Generate tooltip text for the layer.
1682 * This is relatively complicated as it considers information for
1683 * no tracks, a single track or multiple tracks
1684 * (which may or may not have timing information)
1686 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1697 static gchar tmp_buf[128];
1700 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1702 // Safety check - I think these should always be valid
1703 if ( vtl->tracks && vtl->waypoints ) {
1704 tooltip_tracks tt = { 0.0, 0, 0 };
1705 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1707 GDate* gdate_start = g_date_new ();
1708 g_date_set_time_t (gdate_start, tt.start_time);
1710 GDate* gdate_end = g_date_new ();
1711 g_date_set_time_t (gdate_end, tt.end_time);
1713 if ( g_date_compare (gdate_start, gdate_end) ) {
1714 // Dates differ so print range on separate line
1715 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1716 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1717 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1720 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1721 if ( tt.start_time != 0 )
1722 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1726 if ( tt.length > 0.0 ) {
1727 gdouble len_in_units;
1729 // Setup info dependent on distance units
1730 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1731 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1732 len_in_units = VIK_METERS_TO_MILES(tt.length);
1735 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1736 len_in_units = tt.length/1000.0;
1739 // Timing information if available
1741 if ( tt.duration > 0 ) {
1742 g_snprintf (tbuf1, sizeof(tbuf1),
1743 _(" in %d:%02d hrs:mins"),
1744 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1746 g_snprintf (tbuf2, sizeof(tbuf2),
1747 _("\n%sTotal Length %.1f %s%s"),
1748 tbuf3, len_in_units, tbuf4, tbuf1);
1751 // Put together all the elements to form compact tooltip text
1752 g_snprintf (tmp_buf, sizeof(tmp_buf),
1753 _("Tracks: %d - Waypoints: %d%s"),
1754 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1756 g_date_free (gdate_start);
1757 g_date_free (gdate_end);
1764 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1768 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1769 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1770 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1772 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1774 // Could be a better way of handling strings - but this works...
1775 gchar time_buf1[20];
1776 gchar time_buf2[20];
1777 time_buf1[0] = '\0';
1778 time_buf2[0] = '\0';
1779 static gchar tmp_buf[100];
1780 // Compact info: Short date eg (11/20/99), duration and length
1781 // Hopefully these are the things that are most useful and so promoted into the tooltip
1782 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1783 // %x The preferred date representation for the current locale without the time.
1784 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1785 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1786 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1788 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1791 // Get length and consider the appropriate distance units
1792 gdouble tr_len = vik_track_get_length(tr);
1793 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1794 switch (dist_units) {
1795 case VIK_UNITS_DISTANCE_KILOMETRES:
1796 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1798 case VIK_UNITS_DISTANCE_MILES:
1799 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1808 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1810 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1811 // NB It's OK to return NULL
1821 * General layer selection function, find out which bit is selected and take appropriate action
1823 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1826 l->current_wp = NULL;
1827 l->current_wp_name = NULL;
1828 trw_layer_cancel_current_tp ( l, FALSE );
1832 case VIK_TREEVIEW_TYPE_LAYER:
1834 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1835 /* Mark for redraw */
1840 case VIK_TREEVIEW_TYPE_SUBLAYER:
1844 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1846 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1847 /* Mark for redraw */
1851 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1853 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->tracks, sublayer ), l, sublayer );
1854 /* Mark for redraw */
1858 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1860 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
1861 /* Mark for redraw */
1865 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1867 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->waypoints, sublayer ), l, sublayer );
1868 /* Mark for redraw */
1874 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1883 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1888 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1893 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1895 return l->waypoints;
1898 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1900 static VikCoord fixme;
1901 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1902 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1903 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1904 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1905 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1906 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1907 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1908 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1909 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1912 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1915 static VikCoord fixme;
1919 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1920 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1921 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1922 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1923 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1924 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1925 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1926 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1927 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1932 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1934 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1935 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1937 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1938 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1939 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1940 maxmin[0].lat = wpt_maxmin[0].lat;
1943 maxmin[0].lat = trk_maxmin[0].lat;
1945 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1946 maxmin[0].lon = wpt_maxmin[0].lon;
1949 maxmin[0].lon = trk_maxmin[0].lon;
1951 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1952 maxmin[1].lat = wpt_maxmin[1].lat;
1955 maxmin[1].lat = trk_maxmin[1].lat;
1957 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1958 maxmin[1].lon = wpt_maxmin[1].lon;
1961 maxmin[1].lon = trk_maxmin[1].lon;
1965 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1967 /* 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... */
1968 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1969 trw_layer_find_maxmin (vtl, maxmin);
1970 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1974 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1975 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1980 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1983 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1984 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
1986 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1989 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1991 /* First set the center [in case previously viewing from elsewhere] */
1992 /* Then loop through zoom levels until provided positions are in view */
1993 /* This method is not particularly fast - but should work well enough */
1994 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1996 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1997 vik_viewport_set_center_coord ( vvp, &coord );
1999 /* Convert into definite 'smallest' and 'largest' positions */
2000 struct LatLon minmin;
2001 if ( maxmin[0].lat < maxmin[1].lat )
2002 minmin.lat = maxmin[0].lat;
2004 minmin.lat = maxmin[1].lat;
2006 struct LatLon maxmax;
2007 if ( maxmin[0].lon > maxmin[1].lon )
2008 maxmax.lon = maxmin[0].lon;
2010 maxmax.lon = maxmin[1].lon;
2012 /* Never zoom in too far - generally not that useful, as too close ! */
2013 /* Always recalculate the 'best' zoom level */
2015 vik_viewport_set_zoom ( vvp, zoom );
2017 gdouble min_lat, max_lat, min_lon, max_lon;
2018 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2019 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2020 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2021 /* NB I think the logic used in this test to determine if the bounds is within view
2022 fails if track goes across 180 degrees longitude.
2023 Hopefully that situation is not too common...
2024 Mind you viking doesn't really do edge locations to well anyway */
2025 if ( min_lat < minmin.lat &&
2026 max_lat > minmin.lat &&
2027 min_lon < maxmax.lon &&
2028 max_lon > maxmax.lon )
2029 /* Found within zoom level */
2034 vik_viewport_set_zoom ( vvp, zoom );
2038 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2040 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2041 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2042 trw_layer_find_maxmin (vtl, maxmin);
2043 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2046 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2051 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2053 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])) ) ) {
2054 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2057 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2060 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2062 GtkWidget *file_selector;
2064 gboolean failed = FALSE;
2065 file_selector = gtk_file_chooser_dialog_new (title,
2067 GTK_FILE_CHOOSER_ACTION_SAVE,
2068 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2069 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2071 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2073 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2075 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2076 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2078 gtk_widget_hide ( file_selector );
2079 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2084 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2086 gtk_widget_hide ( file_selector );
2087 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2092 gtk_widget_destroy ( file_selector );
2094 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2097 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2099 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2102 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2104 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2107 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2109 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2110 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2111 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2112 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2114 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2116 g_free ( auto_save_name );
2119 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2121 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2122 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2123 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2124 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2126 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2128 g_free ( auto_save_name );
2131 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2133 gpointer layer_and_vlp[2];
2134 layer_and_vlp[0] = pass_along[0];
2135 layer_and_vlp[1] = pass_along[1];
2137 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2138 gchar *auto_save_name = g_strdup ( pass_along[3] );
2139 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2140 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2142 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2144 g_free ( auto_save_name );
2147 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2149 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2150 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2151 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2152 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2154 GTK_RESPONSE_REJECT,
2156 GTK_RESPONSE_ACCEPT,
2159 GtkWidget *label, *entry;
2160 label = gtk_label_new(_("Waypoint Name:"));
2161 entry = gtk_entry_new();
2163 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2164 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2165 gtk_widget_show_all ( label );
2166 gtk_widget_show_all ( entry );
2168 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2170 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2173 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2176 for ( i = strlen(upname)-1; i >= 0; i-- )
2177 upname[i] = toupper(upname[i]);
2179 wp = g_hash_table_lookup ( wps, upname );
2182 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2185 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2186 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2187 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, upname ), TRUE );
2194 gtk_widget_destroy ( dia );
2197 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2199 gchar *default_name = highest_wp_number_get(vtl);
2200 VikWaypoint *wp = vik_waypoint_new();
2201 gchar *returned_name;
2203 wp->coord = *def_coord;
2205 // Attempt to auto set height if DEM data is available
2206 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2207 if ( elev != VIK_DEM_INVALID_ELEVATION )
2208 wp->altitude = (gdouble)elev;
2210 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2213 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2214 g_free (default_name);
2217 g_free (default_name);
2218 vik_waypoint_free(wp);
2222 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2225 struct LatLon one_ll, two_ll;
2226 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2228 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2229 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2230 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2231 VikViewport *vvp = vik_window_viewport(vw);
2232 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2233 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2234 vik_coord_to_latlon(&one, &one_ll);
2235 vik_coord_to_latlon(&two, &two_ll);
2236 if (one_ll.lat > two_ll.lat) {
2237 maxmin[0].lat = one_ll.lat;
2238 maxmin[1].lat = two_ll.lat;
2241 maxmin[0].lat = two_ll.lat;
2242 maxmin[1].lat = one_ll.lat;
2244 if (one_ll.lon > two_ll.lon) {
2245 maxmin[0].lon = one_ll.lon;
2246 maxmin[1].lon = two_ll.lon;
2249 maxmin[0].lon = two_ll.lon;
2250 maxmin[1].lon = one_ll.lon;
2252 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2255 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2257 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2258 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2259 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2261 trw_layer_find_maxmin (vtl, maxmin);
2262 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2265 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2268 * Acquire into this TRW Layer straight from GPS Device
2270 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2272 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2273 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2274 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2275 VikViewport *vvp = vik_window_viewport(vw);
2277 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2278 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2282 * Acquire into this TRW Layer from Google Directions
2284 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2286 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2287 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2288 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2289 VikViewport *vvp = vik_window_viewport(vw);
2291 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2294 #ifdef VIK_CONFIG_GEOCACHES
2296 * Acquire into this TRW Layer from Geocaching.com
2298 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2300 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2301 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2302 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2303 VikViewport *vvp = vik_window_viewport(vw);
2305 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2309 static void trw_layer_new_wp ( gpointer lav[2] )
2311 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2312 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2313 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2314 instead return true if you want to update. */
2315 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 )
2316 vik_layers_panel_emit_update ( vlp );
2319 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2321 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2322 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2324 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2325 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2326 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2327 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2328 vik_layers_panel_emit_update ( vlp );
2332 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2334 /* NB do not care if wp is visible or not */
2335 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2338 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2340 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2341 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2343 /* Only 1 waypoint - jump straight to it */
2344 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2345 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2346 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2348 /* If at least 2 waypoints - find center and then zoom to fit */
2349 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2351 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2352 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2353 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2356 vik_layers_panel_emit_update ( vlp );
2359 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2361 static gpointer pass_along[2];
2363 GtkWidget *export_submenu;
2364 GtkWidget *wikipedia_submenu;
2365 pass_along[0] = vtl;
2366 pass_along[1] = vlp;
2368 item = gtk_menu_item_new();
2369 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2370 gtk_widget_show ( item );
2372 /* Now with icons */
2373 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2374 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2376 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2377 gtk_widget_show ( item );
2379 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2380 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2381 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2382 gtk_widget_show ( item );
2384 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2386 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2387 gtk_widget_show ( item );
2389 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2390 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2391 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2392 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2393 gtk_widget_show ( item );
2395 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2396 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2397 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2398 gtk_widget_show ( item );
2400 export_submenu = gtk_menu_new ();
2401 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2402 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2403 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2404 gtk_widget_show ( item );
2405 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2407 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2408 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2409 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2410 gtk_widget_show ( item );
2412 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2413 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2414 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2415 gtk_widget_show ( item );
2417 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2418 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2419 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2420 gtk_widget_show ( item );
2422 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2423 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2424 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2425 gtk_widget_show ( item );
2427 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2428 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2429 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2430 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2431 gtk_widget_show ( item );
2433 #ifdef VIK_CONFIG_GEONAMES
2434 wikipedia_submenu = gtk_menu_new();
2435 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2436 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2437 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2438 gtk_widget_show(item);
2439 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2441 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2442 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2443 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2444 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2445 gtk_widget_show ( item );
2447 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2448 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2449 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2450 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2451 gtk_widget_show ( item );
2454 GtkWidget *acquire_submenu = gtk_menu_new ();
2455 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2456 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2457 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2458 gtk_widget_show ( item );
2459 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2461 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2462 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2463 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2464 gtk_widget_show ( item );
2466 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2468 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2469 gtk_widget_show ( item );
2471 #ifdef VIK_CONFIG_GEOCACHES
2472 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2473 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2474 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2475 gtk_widget_show ( item );
2478 #ifdef VIK_CONFIG_OPENSTREETMAP
2479 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2480 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2481 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2482 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2483 gtk_widget_show ( item );
2486 GtkWidget *delete_submenu = gtk_menu_new ();
2487 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2488 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2489 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2490 gtk_widget_show ( item );
2491 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2493 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2494 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2495 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2496 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2497 gtk_widget_show ( item );
2499 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2500 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2501 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2502 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2503 gtk_widget_show ( item );
2505 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2506 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2507 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2508 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2509 gtk_widget_show ( item );
2511 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2512 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2513 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2514 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2515 gtk_widget_show ( item );
2517 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2518 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2520 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2521 gtk_widget_show ( item );
2524 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2525 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2527 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2528 gtk_widget_show ( item );
2532 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2534 if ( VIK_LAYER(vtl)->realized )
2536 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2538 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2541 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2542 // Visibility column always needed for waypoints
2543 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2544 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2546 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2548 // Actual setting of visibility dependent on the waypoint
2549 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2550 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2554 highest_wp_number_add_wp(vtl, name);
2555 g_hash_table_insert ( vtl->waypoints, name, wp );
2559 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2561 if ( VIK_LAYER(vtl)->realized )
2563 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2565 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2568 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2569 // Visibility column always needed for tracks
2570 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2571 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2573 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2575 // Actual setting of visibility dependent on the track
2576 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2577 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2581 g_hash_table_insert ( vtl->tracks, name, t );
2585 /* to be called whenever a track has been deleted or may have been changed. */
2586 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2588 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2589 trw_layer_cancel_current_tp ( vtl, FALSE );
2590 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2591 trw_layer_cancel_last_tp ( vtl );
2594 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2597 gchar *newname = g_strdup(name);
2598 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2599 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2600 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2602 newname = new_newname;
2608 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2610 vik_trw_layer_add_waypoint ( vtl,
2611 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2614 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2616 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
2617 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2618 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
2619 vik_track_free ( tr );
2620 vtl->route_finder_append = FALSE; /* this means we have added it */
2622 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2623 vik_trw_layer_add_track ( vtl, new_name, tr );
2625 if ( vtl->route_finder_check_added_track ) {
2626 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2627 if ( vtl->route_finder_added_track_name ) /* for google routes */
2628 g_free ( vtl->route_finder_added_track_name );
2629 vtl->route_finder_added_track_name = g_strdup(new_name);
2634 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2636 *l = g_list_append(*l, (gpointer)name);
2639 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2641 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2642 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2644 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2645 vik_trw_layer_delete_track(vtl_src, name);
2646 vik_trw_layer_add_track(vtl_dest, newname, t);
2648 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2650 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2651 vik_trw_layer_delete_waypoint(vtl_src, name);
2652 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2656 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2658 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2659 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2661 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2662 GList *items = NULL;
2665 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2666 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2668 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2669 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2674 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2675 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2677 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2684 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2685 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2689 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2691 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2692 gboolean was_visible = FALSE;
2696 was_visible = t->visible;
2697 if ( t == vtl->current_track ) {
2698 vtl->current_track = NULL;
2700 if ( t == vtl->route_finder_current_track )
2701 vtl->route_finder_current_track = NULL;
2703 /* could be current_tp, so we have to check */
2704 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2706 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2707 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2708 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2710 /* do this last because trk_name may be pointing to actual orig key */
2711 g_hash_table_remove ( vtl->tracks, trk_name );
2716 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2718 gboolean was_visible = FALSE;
2721 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2725 if ( wp == vtl->current_wp ) {
2726 vtl->current_wp = NULL;
2727 vtl->current_wp_name = NULL;
2728 vtl->moving_wp = FALSE;
2731 was_visible = wp->visible;
2732 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2733 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2734 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2736 highest_wp_number_remove_wp(vtl, wp_name);
2737 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2743 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2745 vik_treeview_item_delete (vt, it );
2748 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2751 vtl->current_track = NULL;
2752 vtl->route_finder_current_track = NULL;
2753 if (vtl->current_tp_track_name)
2754 trw_layer_cancel_current_tp(vtl, FALSE);
2755 if (vtl->last_tp_track_name)
2756 trw_layer_cancel_last_tp ( vtl );
2758 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2759 g_hash_table_remove_all(vtl->tracks_iters);
2760 g_hash_table_remove_all(vtl->tracks);
2762 /* TODO: only update if the layer is visible (ticked) */
2763 vik_layer_emit_update ( VIK_LAYER(vtl) );
2766 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2768 vtl->current_wp = NULL;
2769 vtl->current_wp_name = NULL;
2770 vtl->moving_wp = FALSE;
2772 highest_wp_number_reset(vtl);
2774 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2775 g_hash_table_remove_all(vtl->waypoints_iters);
2776 g_hash_table_remove_all(vtl->waypoints);
2778 /* TODO: only update if the layer is visible (ticked) */
2779 vik_layer_emit_update ( VIK_LAYER(vtl) );
2782 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
2784 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2785 // Get confirmation from the user
2786 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2787 _("Are you sure you want to delete all tracks in %s?"),
2788 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2789 vik_trw_layer_delete_all_tracks (vtl);
2792 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
2794 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2795 // Get confirmation from the user
2796 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2797 _("Are you sure you want to delete all waypoints in %s?"),
2798 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2799 vik_trw_layer_delete_all_waypoints (vtl);
2802 static void trw_layer_delete_item ( gpointer pass_along[6] )
2804 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2805 gboolean was_visible = FALSE;
2806 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2808 if ( GPOINTER_TO_INT ( pass_along[4]) )
2809 // Get confirmation from the user
2810 // Maybe this Waypoint Delete should be optional as is it could get annoying...
2811 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2812 _("Are you sure you want to delete the waypoint \"%s\""),
2815 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2819 if ( GPOINTER_TO_INT ( pass_along[4]) )
2820 // Get confirmation from the user
2821 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2822 _("Are you sure you want to delete the track \"%s\""),
2825 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2828 vik_layer_emit_update ( VIK_LAYER(vtl) );
2832 static void trw_layer_properties_item ( gpointer pass_along[6] )
2834 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2835 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2837 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2840 gboolean updated = FALSE;
2841 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
2843 if ( updated && VIK_LAYER(vtl)->visible )
2844 vik_layer_emit_update ( VIK_LAYER(vtl) );
2849 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2852 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2854 pass_along[1], /* vlp */
2855 pass_along[3], /* track name */
2856 pass_along[5] ); /* vvp */
2862 Parameter 1 -> VikLayersPanel
2863 Parameter 2 -> VikLayer
2864 Parameter 3 -> VikViewport
2866 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2869 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2870 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2873 /* since vlp not set, vl & vvp should be valid instead! */
2875 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2876 vik_layer_emit_update ( VIK_LAYER(vl) );
2881 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
2883 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2884 if ( trps && trps->data )
2885 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2888 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
2890 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2891 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2892 if ( trps && *trps )
2894 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2896 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2897 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2898 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2899 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2900 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
2904 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2906 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2907 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2909 vtl->current_track = track;
2910 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2912 if ( track->trackpoints )
2913 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2917 * extend a track using route finder
2919 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
2921 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2922 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2923 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2925 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2926 vtl->route_finder_coord = last_coord;
2927 vtl->route_finder_current_track = track;
2928 vtl->route_finder_started = TRUE;
2930 if ( track->trackpoints )
2931 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
2935 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2937 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2938 /* Also warn if overwrite old elevation data */
2939 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2941 vik_track_apply_dem_data ( track );
2944 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2946 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2949 trps = g_list_last(trps);
2950 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2953 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
2955 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2958 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2961 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
2963 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2966 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2969 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
2971 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2974 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2978 * Automatically change the viewport to center on the track and zoom to see the extent of the track
2980 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2982 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2983 if ( trps && *trps )
2985 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2986 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2987 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
2988 if ( pass_along[1] )
2989 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2991 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
2995 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
2997 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2998 trw_layer_tpwin_init ( vtl );
3001 /*************************************
3002 * merge/split by time routines
3003 *************************************/
3005 /* called for each key in track hash table.
3006 * If the current track has the same time stamp type, add it to the result,
3007 * except the one pointed by "exclude".
3008 * set exclude to NULL if there is no exclude to check.
3009 * Note that the result is in reverse (for performance reasons).
3014 gboolean with_timestamps;
3016 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3018 twt_udata *user_data = udata;
3019 VikTrackpoint *p1, *p2;
3021 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3025 if (VIK_TRACK(value)->trackpoints) {
3026 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3027 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3029 if ( user_data->with_timestamps ) {
3030 if (!p1->has_timestamp || !p2->has_timestamp) {
3035 // Don't add tracks with timestamps when getting non timestamp tracks
3036 if (p1->has_timestamp || p2->has_timestamp) {
3042 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3045 /* called for each key in track hash table. if original track user_data[1] is close enough
3046 * to the passed one, add it to list in user_data[0]
3048 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3051 VikTrackpoint *p1, *p2;
3053 GList **nearby_tracks = ((gpointer *)user_data)[0];
3054 GList *orig_track = ((gpointer *)user_data)[1];
3055 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3058 * detect reasons for not merging, and return
3059 * if no reason is found not to merge, then do it.
3062 if (VIK_TRACK(value)->trackpoints == orig_track) {
3066 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3067 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3069 if (VIK_TRACK(value)->trackpoints) {
3070 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3071 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3073 if (!p1->has_timestamp || !p2->has_timestamp) {
3074 g_print("no timestamp\n");
3078 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3079 if (! (abs(t1 - p2->timestamp) < thr*60 ||
3081 abs(p1->timestamp - t2) < thr*60)
3088 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3091 /* comparison function used to sort tracks; a and b are hash table keys */
3092 /* Not actively used - can be restored if needed
3093 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3095 GHashTable *tracks = user_data;
3098 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3099 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3101 if (t1 < t2) return -1;
3102 if (t1 > t2) return 1;
3107 /* comparison function used to sort trackpoints */
3108 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3110 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3112 if (t1 < t2) return -1;
3113 if (t1 > t2) return 1;
3117 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3119 * comparison function which can be used to sort tracks or waypoints by name
3121 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3123 const gchar* namea = (const gchar*) a;
3124 const gchar* nameb = (const gchar*) b;
3125 if ( namea == NULL || nameb == NULL)
3128 // Same sort method as used in the vik_treeview_*_alphabetize functions
3129 return strcmp ( namea, nameb );
3134 * Attempt to merge selected track with other tracks specified by the user
3135 * Tracks to merge with must be of the same 'type' as the selected track -
3136 * either all with timestamps, or all without timestamps
3138 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3140 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3141 gchar *orig_track_name = pass_along[3];
3142 GList *other_tracks = NULL;
3143 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3145 if ( !track->trackpoints )
3149 udata.result = &other_tracks;
3150 udata.exclude = track->trackpoints;
3151 // Allow merging with 'similar' time type time tracks
3152 // i.e. either those times, or those without
3153 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3155 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3156 other_tracks = g_list_reverse(other_tracks);
3158 if ( !other_tracks ) {
3159 if ( udata.with_timestamps )
3160 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3162 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3166 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3167 // Sort alphabetically for user presentation
3168 other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3171 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3173 _("Merge with..."), _("Select track to merge with"));
3174 g_list_free(other_tracks);
3179 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3180 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3182 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3183 merge_track->trackpoints = NULL;
3184 vik_trw_layer_delete_track(vtl, l->data);
3185 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3188 /* TODO: free data before free merge_list */
3189 for (l = merge_list; l != NULL; l = g_list_next(l))
3191 g_list_free(merge_list);
3192 vik_layer_emit_update( VIK_LAYER(vtl) );
3196 /* merge by time routine */
3197 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3199 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3200 gchar *orig_track_name = strdup(pass_along[3]);
3203 GList *nearby_tracks;
3206 static guint thr = 1;
3207 guint track_count = 0;
3209 GList *tracks_with_timestamp = NULL;
3210 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3211 if (track->trackpoints &&
3212 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3213 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3214 free(orig_track_name);
3219 udata.result = &tracks_with_timestamp;
3220 udata.exclude = track->trackpoints;
3221 udata.with_timestamps = TRUE;
3222 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3223 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3225 if (!tracks_with_timestamp) {
3226 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3227 free(orig_track_name);
3230 g_list_free(tracks_with_timestamp);
3232 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3233 _("Merge Threshold..."),
3234 _("Merge when time between tracks less than:"),
3236 free(orig_track_name);
3240 /* merge tracks until we can't */
3241 nearby_tracks = NULL;
3245 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3246 trps = track->trackpoints;
3251 if (nearby_tracks) {
3252 g_list_free(nearby_tracks);
3253 nearby_tracks = NULL;
3256 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3257 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3259 /* g_print("Original track times: %d and %d\n", t1, t2); */
3260 params[0] = &nearby_tracks;
3262 params[2] = GUINT_TO_POINTER (thr);
3264 /* get a list of adjacent-in-time tracks */
3265 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3267 /* add original track */
3268 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3272 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3273 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3274 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3275 GList *l = nearby_tracks;
3276 VikTrack *tr = vik_track_new();
3277 tr->visible = track->visible;
3282 t1 = get_first_trackpoint(l)->timestamp;
3283 t2 = get_last_trackpoint(l)->timestamp;
3284 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3288 /* remove trackpoints from merged track, delete track */
3289 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3290 get_track(l)->trackpoints = NULL;
3291 vik_trw_layer_delete_track(vtl, l->data);
3296 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3297 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3299 #undef get_first_trackpoint
3300 #undef get_last_trackpoint
3303 } while (track_count > 1);
3304 g_list_free(nearby_tracks);
3305 free(orig_track_name);
3306 vik_layer_emit_update( VIK_LAYER(vtl) );
3309 /* split by time routine */
3310 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3312 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3313 GList *trps = track->trackpoints;
3315 GList *newlists = NULL;
3316 GList *newtps = NULL;
3318 static guint thr = 1;
3325 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3326 _("Split Threshold..."),
3327 _("Split when time between trackpoints exceeds:"),
3332 /* iterate through trackpoints, and copy them into new lists without touching original list */
3333 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3337 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3339 g_print("panic: ts < prev_ts: this should never happen!\n");
3342 if (ts - prev_ts > thr*60) {
3343 /* flush accumulated trackpoints into new list */
3344 newlists = g_list_append(newlists, g_list_reverse(newtps));
3348 /* accumulate trackpoint copies in newtps, in reverse order */
3349 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3351 iter = g_list_next(iter);
3354 newlists = g_list_append(newlists, g_list_reverse(newtps));
3357 /* put lists of trackpoints into tracks */
3360 // Only bother updating if the split results in new tracks
3361 if (g_list_length (newlists) > 1) {
3366 tr = vik_track_new();
3367 tr->visible = track->visible;
3368 tr->trackpoints = (GList *)(iter->data);
3370 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3371 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3372 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3373 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3375 iter = g_list_next(iter);
3377 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3378 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3380 g_list_free(newlists);
3384 * Split a track by the number of points as specified by the user
3386 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3388 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3389 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3391 // Check valid track
3392 GList *trps = track->trackpoints;
3396 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3397 _("Split Every Nth Point"),
3398 _("Split on every Nth point:"),
3399 250, // Default value as per typical limited track capacity of various GPS devices
3403 // Was a valid number returned?
3409 GList *newlists = NULL;
3410 GList *newtps = NULL;
3415 /* accumulate trackpoint copies in newtps, in reverse order */
3416 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3418 if (count >= points) {
3419 /* flush accumulated trackpoints into new list */
3420 newlists = g_list_append(newlists, g_list_reverse(newtps));
3424 iter = g_list_next(iter);
3427 // If there is a remaining chunk put that into the new split list
3428 // This may well be the whole track if no split points were encountered
3430 newlists = g_list_append(newlists, g_list_reverse(newtps));
3433 /* put lists of trackpoints into tracks */
3436 // Only bother updating if the split results in new tracks
3437 if (g_list_length (newlists) > 1) {
3442 tr = vik_track_new();
3443 tr->visible = track->visible;
3444 tr->trackpoints = (GList *)(iter->data);
3446 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3447 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3449 iter = g_list_next(iter);
3451 // Remove original track and then update the display
3452 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3453 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3455 g_list_free(newlists);
3458 /* end of split/merge routines */
3461 * Similar to trw_layer_enum_item, but this uses a sorted method
3463 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3465 GList **list = (GList**)udata;
3466 //*list = g_list_prepend(*all, key); //unsorted method
3467 // Sort named list alphabetically
3468 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3474 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3476 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3478 // Sort list alphabetically for better presentation
3479 g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3482 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3486 // Get list of items to delete from the user
3487 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3490 _("Delete Selection"),
3491 _("Select tracks to delete"));
3494 // Delete requested tracks
3495 // since specificly requested, IMHO no need for extra confirmation
3496 if ( delete_list ) {
3498 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3499 vik_trw_layer_delete_track(vtl, l->data);
3501 g_list_free(delete_list);
3502 vik_layer_emit_update( VIK_LAYER(vtl) );
3509 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3511 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3514 // Sort list alphabetically for better presentation
3515 g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3517 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3521 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3523 // Get list of items to delete from the user
3524 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3527 _("Delete Selection"),
3528 _("Select waypoints to delete"));
3531 // Delete requested waypoints
3532 // since specificly requested, IMHO no need for extra confirmation
3533 if ( delete_list ) {
3535 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3536 vik_trw_layer_delete_waypoint(vtl, l->data);
3538 g_list_free(delete_list);
3539 vik_layer_emit_update( VIK_LAYER(vtl) );
3544 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3546 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3548 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3551 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3553 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3554 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3558 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3560 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3565 if (strcmp(newname, sublayer) == 0 )
3568 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3569 if (g_hash_table_lookup( l->waypoints, newname))
3571 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3576 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3577 g_hash_table_steal ( l->waypoints_iters, sublayer );
3579 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3580 highest_wp_number_remove_wp(l, sublayer);
3581 g_hash_table_remove ( l->waypoints, sublayer );
3583 rv = g_strdup(newname);
3585 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3587 highest_wp_number_add_wp(l, rv);
3588 g_hash_table_insert ( l->waypoints, rv, wp );
3589 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3591 /* it hasn't been updated yet so we pass new name */
3592 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3593 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3596 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3599 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3606 if (strcmp(newname, sublayer) == 0)
3609 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3610 if (g_hash_table_lookup( l->tracks, newname))
3612 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3617 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3618 g_hash_table_steal ( l->tracks, sublayer );
3620 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3621 g_hash_table_steal ( l->tracks_iters, sublayer );
3623 rv = g_strdup(newname);
3625 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3627 g_hash_table_insert ( l->tracks, rv, tr );
3628 g_hash_table_insert ( l->tracks_iters, rv, iter );
3630 /* don't forget about current_tp_track_name, update that too */
3631 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3633 l->current_tp_track_name = rv;
3635 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3637 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3638 l->last_tp_track_name = rv;
3640 g_free ( orig_key );
3642 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3643 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3646 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3652 static gboolean is_valid_geocache_name ( gchar *str )
3654 gint len = strlen ( str );
3655 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]));
3658 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3660 gchar *track_name = (gchar *) pass_along[3];
3661 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3662 a_acquire_set_filter_track ( tr, track_name );
3665 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3667 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3668 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3671 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3673 gchar *track_name = (gchar *) pass_along[3];
3674 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3676 gchar *escaped = uri_escape ( tr->comment );
3677 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3678 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3684 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3685 /* viewpoint is now available instead */
3686 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3688 static gpointer pass_along[6];
3690 gboolean rv = FALSE;
3693 pass_along[1] = vlp;
3694 pass_along[2] = GINT_TO_POINTER (subtype);
3695 pass_along[3] = sublayer;
3696 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
3697 pass_along[5] = vvp;
3699 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3703 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3705 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3706 gtk_widget_show ( item );
3708 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3709 VikTrwLayer *vtl = l;
3710 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3711 if (tr && tr->property_dialog)
3712 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3715 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3716 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3717 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3718 gtk_widget_show ( item );
3720 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3721 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3722 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3723 gtk_widget_show ( item );
3725 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3726 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3727 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3728 gtk_widget_show ( item );
3730 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3732 gboolean separator_created = FALSE;
3734 /* could be a right-click using the tool */
3735 if ( vlp != NULL ) {
3736 item = gtk_menu_item_new ();
3737 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3738 gtk_widget_show ( item );
3740 separator_created = TRUE;
3742 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
3743 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3744 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3745 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3746 gtk_widget_show ( item );
3749 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3751 if ( !separator_created ) {
3752 item = gtk_menu_item_new ();
3753 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3754 gtk_widget_show ( item );
3755 separator_created = TRUE;
3758 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3759 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3760 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3761 gtk_widget_show ( item );
3764 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
3766 if ( wp && wp->image )
3768 if ( !separator_created ) {
3769 item = gtk_menu_item_new ();
3770 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3771 gtk_widget_show ( item );
3772 separator_created = TRUE;
3775 // Set up image paramater
3776 pass_along[5] = wp->image;
3778 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
3779 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
3780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
3781 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3782 gtk_widget_show ( item );
3788 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3791 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3792 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3794 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3795 gtk_widget_show ( item );
3798 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
3800 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
3801 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3803 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3804 gtk_widget_show ( item );
3806 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3807 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3809 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3810 gtk_widget_show ( item );
3812 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
3813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3815 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3816 gtk_widget_show ( item );
3818 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
3819 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3820 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3821 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3822 gtk_widget_show ( item );
3825 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
3829 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
3830 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3832 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3833 gtk_widget_show ( item );
3835 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
3836 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3837 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3838 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3839 gtk_widget_show ( item );
3841 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
3842 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3843 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3844 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3845 gtk_widget_show ( item );
3848 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3850 GtkWidget *goto_submenu;
3851 item = gtk_menu_item_new ();
3852 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3853 gtk_widget_show ( item );
3855 goto_submenu = gtk_menu_new ();
3856 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
3857 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3858 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3859 gtk_widget_show ( item );
3860 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3862 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
3863 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
3864 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3865 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3866 gtk_widget_show ( item );
3868 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3869 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3870 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3871 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3872 gtk_widget_show ( item );
3874 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
3875 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
3876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3877 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3878 gtk_widget_show ( item );
3880 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3881 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
3882 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3883 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3884 gtk_widget_show ( item );
3886 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3887 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
3888 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3889 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3890 gtk_widget_show ( item );
3892 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3893 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
3894 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3895 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3896 gtk_widget_show ( item );
3898 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
3899 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3900 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3901 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3902 gtk_widget_show ( item );
3904 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
3905 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3906 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3907 gtk_widget_show ( item );
3909 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3911 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3912 gtk_widget_show ( item );
3914 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
3915 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3916 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3917 gtk_widget_show ( item );
3919 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
3920 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
3921 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3922 gtk_widget_show ( item );
3924 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
3926 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3927 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
3928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3929 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3930 gtk_widget_show ( item );
3933 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3934 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("DEM Download/Import", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
3935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3936 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3937 gtk_widget_show ( item );
3939 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
3940 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3941 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3942 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3943 gtk_widget_show ( item );
3945 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3946 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3947 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3948 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3949 gtk_widget_show ( item );
3951 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
3952 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
3953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
3954 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3955 gtk_widget_show ( item );
3957 #ifdef VIK_CONFIG_OPENSTREETMAP
3958 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3960 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3961 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3962 gtk_widget_show ( item );
3965 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3967 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3968 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
3969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3970 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3971 gtk_widget_show ( item );
3974 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
3975 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3976 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3977 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3978 gtk_widget_show ( item );
3980 /* ATM This function is only available via the layers panel, due to needing a vlp */
3982 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3983 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3984 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3986 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3987 gtk_widget_show ( item );
3991 // Only show on viewport popmenu when a trackpoint is selected
3992 if ( ! vlp && l->current_tpl ) {
3994 item = gtk_menu_item_new ();
3995 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3996 gtk_widget_show ( item );
3998 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
3999 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4001 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4002 gtk_widget_show ( item );
4010 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4013 if (!vtl->current_tpl)
4015 if (!vtl->current_tpl->next)
4018 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4019 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4021 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4024 VikTrackpoint *tp_new = vik_trackpoint_new();
4025 struct LatLon ll_current, ll_next;
4026 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4027 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4029 /* main positional interpolation */
4030 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4031 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4033 /* Now other properties that can be interpolated */
4034 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4036 if (tp_current->has_timestamp && tp_next->has_timestamp) {
4037 /* Note here the division is applied to each part, then added
4038 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4039 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4040 tp_new->has_timestamp = TRUE;
4043 if (tp_current->speed != NAN && tp_next->speed != NAN)
4044 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4046 /* TODO - improve interpolation of course, as it may not be correct.
4047 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4048 [similar applies if value is in radians] */
4049 if (tp_current->course != NAN && tp_next->course != NAN)
4050 tp_new->speed = (tp_current->course + tp_next->course)/2;
4052 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4054 /* Insert new point into the trackpoints list after the current TP */
4055 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4056 gint index = g_list_index ( tr->trackpoints, tp_current );
4058 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4063 /* to be called when last_tpl no long exists. */
4064 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4066 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4067 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4068 vtl->last_tpl = NULL;
4069 vtl->last_tp_track_name = NULL;
4072 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4078 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4082 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4084 if ( vtl->current_tpl )
4086 vtl->current_tpl = NULL;
4087 vtl->current_tp_track_name = NULL;
4088 vik_layer_emit_update(VIK_LAYER(vtl));
4092 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4094 g_assert ( vtl->tpwin != NULL );
4095 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4096 trw_layer_cancel_current_tp ( vtl, TRUE );
4098 if ( vtl->current_tpl == NULL )
4101 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4103 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4104 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4106 VikTrack *tr = vik_track_new ();
4107 GList *newglist = g_list_alloc ();
4108 newglist->prev = NULL;
4109 newglist->next = vtl->current_tpl->next;
4110 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4111 tr->trackpoints = newglist;
4113 vtl->current_tpl->next->prev = newglist; /* end old track here */
4114 vtl->current_tpl->next = NULL;
4116 vtl->current_tpl = newglist; /* change tp to first of new track. */
4117 vtl->current_tp_track_name = name;
4119 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4123 vik_trw_layer_add_track ( vtl, name, tr );
4124 vik_layer_emit_update(VIK_LAYER(vtl));
4127 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4129 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4131 g_assert(tr != NULL);
4133 /* can't join with a non-existent trackpoint */
4134 vtl->last_tpl = NULL;
4135 vtl->last_tp_track_name = NULL;
4137 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4139 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4140 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4142 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4144 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4145 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4147 trw_layer_cancel_last_tp ( vtl );
4149 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4150 g_list_free_1 ( vtl->current_tpl );
4151 vtl->current_tpl = new_tpl;
4152 vik_layer_emit_update(VIK_LAYER(vtl));
4156 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4157 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4158 g_list_free_1 ( vtl->current_tpl );
4159 trw_layer_cancel_current_tp ( vtl, FALSE );
4162 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4164 vtl->last_tpl = vtl->current_tpl;
4165 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4166 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
4168 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4170 vtl->last_tpl = vtl->current_tpl;
4171 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4172 vik_layer_emit_update(VIK_LAYER(vtl));
4174 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4176 // Check tracks exist and are different before joining
4177 if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4180 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4181 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4183 VikTrack *tr_first = tr1, *tr_last = tr2;
4187 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4188 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4189 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4190 vik_track_reverse ( tr1 );
4191 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4196 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4198 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. */
4199 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4200 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4201 tr2->trackpoints = NULL;
4203 tmp = vtl->current_tp_track_name;
4205 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4206 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4208 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4209 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4210 vik_trw_layer_delete_track ( vtl, tmp );
4212 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4213 vik_layer_emit_update(VIK_LAYER(vtl));
4215 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4217 trw_layer_insert_tp_after_current_tp ( vtl );
4218 vik_layer_emit_update(VIK_LAYER(vtl));
4220 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4221 vik_layer_emit_update (VIK_LAYER(vtl));
4224 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4228 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4229 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4230 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4231 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4232 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4234 if ( vtl->current_tpl )
4235 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4236 /* set layer name and TP data */
4239 /***************************************************************************
4241 ***************************************************************************/
4243 /*** Utility data structures and functions ****/
4247 gint closest_x, closest_y;
4248 gchar *closest_wp_name;
4249 VikWaypoint *closest_wp;
4255 gint closest_x, closest_y;
4256 gchar *closest_track_name;
4257 VikTrackpoint *closest_tp;
4262 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4268 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4270 // If waypoint has an image then use the image size to select
4272 gint slackx, slacky;
4273 slackx = wp->image_width / 2;
4274 slacky = wp->image_height / 2;
4276 if ( x <= params->x + slackx && x >= params->x - slackx
4277 && y <= params->y + slacky && y >= params->y - slacky ) {
4278 params->closest_wp_name = name;
4279 params->closest_wp = wp;
4280 params->closest_x = x;
4281 params->closest_y = y;
4284 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4285 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
4286 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4288 params->closest_wp_name = name;
4289 params->closest_wp = wp;
4290 params->closest_x = x;
4291 params->closest_y = y;
4295 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4297 GList *tpl = t->trackpoints;
4306 tp = VIK_TRACKPOINT(tpl->data);
4308 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4310 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4311 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
4312 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4314 params->closest_track_name = name;
4315 params->closest_tp = tp;
4316 params->closest_tpl = tpl;
4317 params->closest_x = x;
4318 params->closest_y = y;
4324 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4326 TPSearchParams params;
4330 params.closest_track_name = NULL;
4331 params.closest_tp = NULL;
4332 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4333 return params.closest_tp;
4336 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4338 WPSearchParams params;
4342 params.closest_wp = NULL;
4343 params.closest_wp_name = NULL;
4344 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4345 return params.closest_wp;
4348 // Some forward declarations
4349 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4350 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4351 static void marker_end_move ( tool_ed_t *t );
4354 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4358 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4360 // Here always allow snapping back to the original location
4361 // this is useful when one decides not to move the thing afterall
4362 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4365 if ( event->state & GDK_CONTROL_MASK )
4367 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4369 new_coord = tp->coord;
4373 if ( event->state & GDK_SHIFT_MASK )
4375 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4377 new_coord = wp->coord;
4381 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4383 marker_moveto ( t, x, y );
4390 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4392 if ( t->holding && event->button == 1 )
4395 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4398 if ( event->state & GDK_CONTROL_MASK )
4400 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4402 new_coord = tp->coord;
4406 if ( event->state & GDK_SHIFT_MASK )
4408 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4410 new_coord = wp->coord;
4413 marker_end_move ( t );
4415 // Determine if working on a waypoint or a trackpoint
4416 if ( t->is_waypoint )
4417 vtl->current_wp->coord = new_coord;
4419 if ( vtl->current_tpl ) {
4420 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4423 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4425 // Don't really know what this is for but seems like it might be handy...
4426 /* can't join with itself! */
4427 trw_layer_cancel_last_tp ( vtl );
4432 vtl->current_wp = NULL;
4433 vtl->current_wp_name = NULL;
4434 trw_layer_cancel_current_tp ( vtl, FALSE );
4436 vik_layer_emit_update ( VIK_LAYER(vtl) );
4443 Returns true if a waypoint or track is found near the requested event position for this particular layer
4444 The item found is automatically selected
4445 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4447 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4449 if ( event->button != 1 )
4452 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4455 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4458 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4460 if (vtl->waypoints_visible) {
4461 WPSearchParams wp_params;
4462 wp_params.vvp = vvp;
4463 wp_params.x = event->x;
4464 wp_params.y = event->y;
4465 wp_params.closest_wp_name = NULL;
4466 wp_params.closest_wp = NULL;
4468 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4470 if ( wp_params.closest_wp ) {
4473 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4475 // Too easy to move it so must be holding shift to start immediately moving it
4476 // or otherwise be previously selected
4477 if ( event->state & GDK_SHIFT_MASK ||
4478 vtl->current_wp == wp_params.closest_wp ) {
4479 // Put into 'move buffer'
4480 // NB vvp & vw already set in tet
4481 tet->vtl = (gpointer)vtl;
4482 tet->is_waypoint = TRUE;
4484 marker_begin_move (tet, event->x, event->y);
4487 vtl->current_wp = wp_params.closest_wp;
4488 vtl->current_wp_name = wp_params.closest_wp_name;
4490 vik_layer_emit_update ( VIK_LAYER(vtl) );
4496 if (vtl->tracks_visible) {
4497 TPSearchParams tp_params;
4498 tp_params.vvp = vvp;
4499 tp_params.x = event->x;
4500 tp_params.y = event->y;
4501 tp_params.closest_track_name = NULL;
4502 tp_params.closest_tp = NULL;
4504 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4506 if ( tp_params.closest_tp ) {
4508 // Always select + highlight the track
4509 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4511 tet->is_waypoint = FALSE;
4513 // Select the Trackpoint
4514 // Can move it immediately when control held or it's the previously selected tp
4515 if ( event->state & GDK_CONTROL_MASK ||
4516 vtl->current_tpl == tp_params.closest_tpl ) {
4517 // Put into 'move buffer'
4518 // NB vvp & vw already set in tet
4519 tet->vtl = (gpointer)vtl;
4520 marker_begin_move (tet, event->x, event->y);
4523 vtl->current_tpl = tp_params.closest_tpl;
4524 vtl->current_tp_track_name = tp_params.closest_track_name;
4527 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4529 vik_layer_emit_update ( VIK_LAYER(vtl) );
4534 /* these aren't the droids you're looking for */
4535 vtl->current_wp = NULL;
4536 vtl->current_wp_name = NULL;
4537 trw_layer_cancel_current_tp ( vtl, FALSE );
4542 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4544 if ( event->button != 3 )
4547 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4550 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4553 /* Post menu for the currently selected item */
4555 /* See if a track is selected */
4556 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4557 if ( track && track->visible ) {
4559 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4561 if ( vtl->track_right_click_menu )
4562 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4564 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4566 trw_layer_sublayer_add_menu_items ( vtl,
4567 vtl->track_right_click_menu,
4569 VIK_TRW_LAYER_SUBLAYER_TRACK,
4570 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4571 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4574 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4580 /* See if a waypoint is selected */
4581 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4582 if ( waypoint && waypoint->visible ) {
4583 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4585 if ( vtl->wp_right_click_menu )
4586 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4588 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4589 trw_layer_sublayer_add_menu_items ( vtl,
4590 vtl->wp_right_click_menu,
4592 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4593 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4594 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4596 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4605 /* background drawing hook, to be passed the viewport */
4606 static gboolean tool_sync_done = TRUE;
4608 static gboolean tool_sync(gpointer data)
4610 VikViewport *vvp = data;
4611 gdk_threads_enter();
4612 vik_viewport_sync(vvp);
4613 tool_sync_done = TRUE;
4614 gdk_threads_leave();
4618 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4621 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4622 gdk_gc_set_function ( t->gc, GDK_INVERT );
4623 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4624 vik_viewport_sync(t->vvp);
4629 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4631 VikViewport *vvp = t->vvp;
4632 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4633 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4637 if (tool_sync_done) {
4638 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4639 tool_sync_done = FALSE;
4643 static void marker_end_move ( tool_ed_t *t )
4645 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4646 g_object_unref ( t->gc );
4650 /*** Edit waypoint ****/
4652 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4654 tool_ed_t *t = g_new(tool_ed_t, 1);
4660 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4662 WPSearchParams params;
4663 tool_ed_t *t = data;
4664 VikViewport *vvp = t->vvp;
4666 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4673 if ( !vtl->vl.visible || !vtl->waypoints_visible )
4676 if ( vtl->current_wp && vtl->current_wp->visible )
4678 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
4680 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
4682 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
4683 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
4685 if ( event->button == 3 )
4686 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4688 marker_begin_move(t, event->x, event->y);
4695 params.x = event->x;
4696 params.y = event->y;
4697 params.closest_wp_name = NULL;
4698 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4699 params.closest_wp = NULL;
4700 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4701 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
4703 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
4704 marker_begin_move(t, event->x, event->y);
4705 g_critical("shouldn't be here");
4708 else if ( params.closest_wp )
4710 if ( event->button == 3 )
4711 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4713 vtl->waypoint_rightclick = FALSE;
4715 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
4717 vtl->current_wp = params.closest_wp;
4718 vtl->current_wp_name = params.closest_wp_name;
4720 /* could make it so don't update if old WP is off screen and new is null but oh well */
4721 vik_layer_emit_update ( VIK_LAYER(vtl) );
4725 vtl->current_wp = NULL;
4726 vtl->current_wp_name = NULL;
4727 vtl->waypoint_rightclick = FALSE;
4728 vik_layer_emit_update ( VIK_LAYER(vtl) );
4732 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4734 tool_ed_t *t = data;
4735 VikViewport *vvp = t->vvp;
4737 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4742 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4745 if ( event->state & GDK_CONTROL_MASK )
4747 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4749 new_coord = tp->coord;
4753 if ( event->state & GDK_SHIFT_MASK )
4755 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4756 if ( wp && wp != vtl->current_wp )
4757 new_coord = wp->coord;
4762 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4764 marker_moveto ( t, x, y );
4771 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4773 tool_ed_t *t = data;
4774 VikViewport *vvp = t->vvp;
4776 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4779 if ( t->holding && event->button == 1 )
4782 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4785 if ( event->state & GDK_CONTROL_MASK )
4787 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4789 new_coord = tp->coord;
4793 if ( event->state & GDK_SHIFT_MASK )
4795 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4796 if ( wp && wp != vtl->current_wp )
4797 new_coord = wp->coord;
4800 marker_end_move ( t );
4802 vtl->current_wp->coord = new_coord;
4803 vik_layer_emit_update ( VIK_LAYER(vtl) );
4806 /* PUT IN RIGHT PLACE!!! */
4807 if ( event->button == 3 && vtl->waypoint_rightclick )
4809 if ( vtl->wp_right_click_menu )
4810 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
4811 if ( vtl->current_wp ) {
4812 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4813 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 );
4814 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4816 vtl->waypoint_rightclick = FALSE;
4821 /**** Begin track ***/
4822 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4827 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4829 vtl->current_track = NULL;
4830 return tool_new_track_click ( vtl, event, vvp );
4833 /*** New track ****/
4835 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4843 gint x1,y1,x2,y2,x3,y3;
4845 } new_track_move_passalong_t;
4847 /* sync and undraw, but only when we have time */
4848 static gboolean ct_sync ( gpointer passalong )
4850 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4852 vik_viewport_sync ( p->vvp );
4853 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4854 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4855 vik_viewport_draw_string (p->vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), p->vtl->current_track_gc, p->x3, p->y3, p->str);
4856 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4858 g_free ( (gpointer) p->str ) ;
4859 p->vtl->ct_sync_done = TRUE;
4864 static const gchar* distance_string (gdouble distance)
4868 /* draw label with distance */
4869 vik_units_distance_t dist_units = a_vik_get_units_distance ();
4870 switch (dist_units) {
4871 case VIK_UNITS_DISTANCE_MILES:
4872 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
4873 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
4874 } else if (distance < 1609.4) {
4875 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
4877 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
4881 // VIK_UNITS_DISTANCE_KILOMETRES
4882 if (distance >= 1000 && distance < 100000) {
4883 g_sprintf(str, "%3.2f km", distance/1000.0);
4884 } else if (distance < 1000) {
4885 g_sprintf(str, "%d m", (int)distance);
4887 g_sprintf(str, "%d km", (int)distance/1000);
4891 return g_strdup (str);
4895 * Actually set the message in statusbar
4897 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
4899 // Only show elevation data when track has some elevation properties
4900 gchar str_gain_loss[64];
4901 str_gain_loss[0] = '\0';
4903 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
4904 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
4905 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
4907 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
4910 // Write with full gain/loss information
4911 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
4912 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
4917 * Figure out what information should be set in the statusbar and then write it
4919 static void update_statusbar ( VikTrwLayer *vtl )
4921 // Get elevation data
4922 gdouble elev_gain, elev_loss;
4923 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
4925 /* Find out actual distance of current track */
4926 gdouble distance = vik_track_get_length (vtl->current_track);
4927 const gchar *str = distance_string (distance);
4929 statusbar_write (str, elev_gain, elev_loss, vtl);
4931 g_free ((gpointer)str);
4935 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
4937 /* if we haven't sync'ed yet, we don't have time to do more. */
4938 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
4939 GList *iter = vtl->current_track->trackpoints;
4940 new_track_move_passalong_t *passalong;
4943 while ( iter->next )
4945 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
4946 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
4947 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
4949 /* Find out actual distance of current track */
4950 gdouble distance = vik_track_get_length (vtl->current_track);
4952 // Now add distance to where the pointer is //
4955 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
4956 vik_coord_to_latlon ( &coord, &ll );
4957 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
4959 // Get elevation data
4960 gdouble elev_gain, elev_loss;
4961 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
4963 // Adjust elevation data (if available) for the current pointer position
4965 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
4966 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
4967 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
4968 // Adjust elevation of last track point
4969 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
4971 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
4974 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
4978 const gchar *str = distance_string (distance);
4980 /* offset from cursor a bit */
4983 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
4984 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
4986 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
4988 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
4989 passalong->vtl = vtl;
4990 passalong->vvp = vvp;
4993 passalong->x2 = event->x;
4994 passalong->y2 = event->y;
4997 passalong->str = str;
4999 // Update statusbar with full gain/loss information
5000 statusbar_write (str, elev_gain, elev_loss, vtl);
5002 /* this will sync and undraw when we have time to */
5003 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5004 vtl->ct_sync_done = FALSE;
5005 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5007 return VIK_LAYER_TOOL_ACK;
5010 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5012 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5013 vtl->current_track = NULL;
5014 vik_layer_emit_update ( VIK_LAYER(vtl) );
5016 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5018 if ( vtl->current_track->trackpoints )
5020 GList *last = g_list_last(vtl->current_track->trackpoints);
5021 g_free ( last->data );
5022 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5025 update_statusbar ( vtl );
5027 vik_layer_emit_update ( VIK_LAYER(vtl) );
5033 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5037 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5040 if ( event->button == 3 && vtl->current_track )
5043 if ( vtl->current_track->trackpoints )
5045 GList *last = g_list_last(vtl->current_track->trackpoints);
5046 g_free ( last->data );
5047 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5049 update_statusbar ( vtl );
5051 vik_layer_emit_update ( VIK_LAYER(vtl) );
5055 if ( event->type == GDK_2BUTTON_PRESS )
5057 /* subtract last (duplicate from double click) tp then end */
5058 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5060 GList *last = g_list_last(vtl->current_track->trackpoints);
5061 g_free ( last->data );
5062 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5063 /* undo last, then end */
5064 vtl->current_track = NULL;
5066 vik_layer_emit_update ( VIK_LAYER(vtl) );
5070 if ( ! vtl->current_track )
5072 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5073 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5075 vtl->current_track = vik_track_new();
5076 vtl->current_track->visible = TRUE;
5077 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5079 /* incase it was created by begin track */
5080 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5085 tp = vik_trackpoint_new();
5086 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5088 /* snap to other TP */
5089 if ( event->state & GDK_CONTROL_MASK )
5091 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5093 tp->coord = other_tp->coord;
5096 tp->newsegment = FALSE;
5097 tp->has_timestamp = FALSE;
5099 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5100 /* Auto attempt to get elevation from DEM data (if it's available) */
5101 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5103 vtl->ct_x1 = vtl->ct_x2;
5104 vtl->ct_y1 = vtl->ct_y2;
5105 vtl->ct_x2 = event->x;
5106 vtl->ct_y2 = event->y;
5108 vik_layer_emit_update ( VIK_LAYER(vtl) );
5112 /*** New waypoint ****/
5114 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5119 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5122 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5124 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5125 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5126 vik_layer_emit_update ( VIK_LAYER(vtl) );
5131 /*** Edit trackpoint ****/
5133 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5135 tool_ed_t *t = g_new(tool_ed_t, 1);
5141 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5143 tool_ed_t *t = data;
5144 VikViewport *vvp = t->vvp;
5145 TPSearchParams params;
5146 /* OUTDATED DOCUMENTATION:
5147 find 5 pixel range on each side. then put these UTM, and a pointer
5148 to the winning track name (and maybe the winning track itself), and a
5149 pointer to the winning trackpoint, inside an array or struct. pass
5150 this along, do a foreach on the tracks which will do a foreach on the
5153 params.x = event->x;
5154 params.y = event->y;
5155 params.closest_track_name = NULL;
5156 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5157 params.closest_tp = NULL;
5159 if ( event->button != 1 )
5162 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5165 if ( !vtl->vl.visible || !vtl->tracks_visible )
5168 if ( vtl->current_tpl )
5170 /* 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.) */
5171 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5172 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5174 g_assert ( current_tr );
5176 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5178 if ( current_tr->visible &&
5179 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5180 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5181 marker_begin_move ( t, event->x, event->y );
5185 vtl->last_tpl = vtl->current_tpl;
5186 vtl->last_tp_track_name = vtl->current_tp_track_name;
5189 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5191 if ( params.closest_tp )
5193 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5194 vtl->current_tpl = params.closest_tpl;
5195 vtl->current_tp_track_name = params.closest_track_name;
5196 trw_layer_tpwin_init ( vtl );
5197 vik_layer_emit_update ( VIK_LAYER(vtl) );
5201 /* these aren't the droids you're looking for */
5205 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5207 tool_ed_t *t = data;
5208 VikViewport *vvp = t->vvp;
5210 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5216 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5219 if ( event->state & GDK_CONTROL_MASK )
5221 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5222 if ( tp && tp != vtl->current_tpl->data )
5223 new_coord = tp->coord;
5225 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5228 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5229 marker_moveto ( t, x, y );
5237 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5239 tool_ed_t *t = data;
5240 VikViewport *vvp = t->vvp;
5242 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5244 if ( event->button != 1)
5249 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5252 if ( event->state & GDK_CONTROL_MASK )
5254 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5255 if ( tp && tp != vtl->current_tpl->data )
5256 new_coord = tp->coord;
5259 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5261 marker_end_move ( t );
5263 /* diff dist is diff from orig */
5265 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5266 /* can't join with itself! */
5267 trw_layer_cancel_last_tp ( vtl );
5269 vik_layer_emit_update ( VIK_LAYER(vtl) );
5276 /*** Route Finder ***/
5277 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5282 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5285 if ( !vtl ) return FALSE;
5286 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5287 if ( event->button == 3 && vtl->route_finder_current_track ) {
5289 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5291 vtl->route_finder_coord = *new_end;
5293 vik_layer_emit_update ( VIK_LAYER(vtl) );
5294 /* remove last ' to:...' */
5295 if ( vtl->route_finder_current_track->comment ) {
5296 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5297 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5298 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5299 last_to - vtl->route_finder_current_track->comment - 1);
5300 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5305 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5306 struct LatLon start, end;
5307 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5308 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5311 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5312 vik_coord_to_latlon ( &(tmp), &end );
5313 vtl->route_finder_coord = tmp; /* for continuations */
5315 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5316 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5317 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
5319 vtl->route_finder_check_added_track = TRUE;
5320 vtl->route_finder_started = FALSE;
5323 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5324 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5325 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5326 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5327 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5328 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5331 /* see if anything was done -- a track was added or appended to */
5332 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5335 tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5338 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5340 vtl->route_finder_current_track = tr;
5342 g_free ( vtl->route_finder_added_track_name );
5343 vtl->route_finder_added_track_name = NULL;
5344 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5345 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5346 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5347 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5349 vtl->route_finder_check_added_track = FALSE;
5350 vtl->route_finder_append = FALSE;
5352 vik_layer_emit_update ( VIK_LAYER(vtl) );
5354 vtl->route_finder_started = TRUE;
5355 vtl->route_finder_coord = tmp;
5356 vtl->route_finder_current_track = NULL;
5361 /*** Show picture ****/
5363 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5368 /* Params are: vvp, event, last match found or NULL */
5369 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
5371 if ( wp->image && wp->visible )
5373 gint x, y, slackx, slacky;
5374 GdkEventButton *event = (GdkEventButton *) params[1];
5376 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5377 slackx = wp->image_width / 2;
5378 slacky = wp->image_height / 2;
5379 if ( x <= event->x + slackx && x >= event->x - slackx
5380 && y <= event->y + slacky && y >= event->y - slacky )
5382 params[2] = wp->image; /* we've found a match. however continue searching
5383 * since we want to find the last match -- that
5384 * is, the match that was drawn last. */
5389 static void trw_layer_show_picture ( gpointer pass_along[6] )
5391 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5393 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5396 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5397 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
5398 g_free ( quoted_file );
5399 if ( ! g_spawn_command_line_async ( cmd, &err ) )
5401 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch eog to open file.") );
5402 g_error_free ( err );
5405 #endif /* WINDOWS */
5408 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5410 gpointer params[3] = { vvp, event, NULL };
5411 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5413 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5416 static gpointer pass_along[6];
5417 pass_along[0] = vtl;
5418 pass_along[5] = params[2];
5419 trw_layer_show_picture ( pass_along );
5420 return TRUE; /* found a match */
5423 return FALSE; /* go through other layers, searching for a match */
5426 /***************************************************************************
5428 ***************************************************************************/
5434 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5436 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5437 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5440 /* Structure for thumbnail creating data used in the background thread */
5442 VikTrwLayer *vtl; // Layer needed for redrawing
5443 GSList *pics; // Image list
5444 } thumbnail_create_thread_data;
5446 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5448 guint total = g_slist_length(tctd->pics), done = 0;
5449 while ( tctd->pics )
5451 a_thumbnails_create ( (gchar *) tctd->pics->data );
5452 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5454 return -1; /* Abort thread */
5456 tctd->pics = tctd->pics->next;
5459 // Redraw to show the thumbnails as they are now created
5460 gdk_threads_enter();
5461 if ( IS_VIK_LAYER(tctd->vtl) )
5462 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) );
5463 gdk_threads_leave();
5468 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5470 while ( tctd->pics )
5472 g_free ( tctd->pics->data );
5473 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5478 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5480 if ( ! vtl->has_verified_thumbnails )
5482 GSList *pics = NULL;
5483 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5486 gint len = g_slist_length ( pics );
5487 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5488 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5491 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5493 (vik_thr_func) create_thumbnails_thread,
5495 (vik_thr_free_func) thumbnail_create_thread_free,
5503 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5505 return vtl->coord_mode;
5510 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5512 vik_coord_convert ( &(wp->coord), *dest_mode );
5515 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5517 vik_track_convert ( tr, *dest_mode );
5520 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5522 if ( vtl->coord_mode != dest_mode )
5524 vtl->coord_mode = dest_mode;
5525 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5526 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5530 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5532 return g_hash_table_lookup ( vtl->waypoints, name );
5535 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5537 return g_hash_table_lookup ( vtl->tracks, name );
5540 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5542 vtl->menu_selection = selection;
5545 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5547 return (vtl->menu_selection);
5550 /* ----------- Downloading maps along tracks --------------- */
5552 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5554 /* TODO: calculating based on current size of viewport */
5555 const gdouble w_at_zoom_0_125 = 0.0013;
5556 const gdouble h_at_zoom_0_125 = 0.0011;
5557 gdouble zoom_factor = zoom_level/0.125;
5559 wh->lat = h_at_zoom_0_125 * zoom_factor;
5560 wh->lon = w_at_zoom_0_125 * zoom_factor;
5562 return 0; /* all OK */
5565 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5567 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5568 (dist->lat >= ABS(to->north_south - from->north_south)))
5571 VikCoord *coord = g_malloc(sizeof(VikCoord));
5572 coord->mode = VIK_COORD_LATLON;
5574 if (ABS(gradient) < 1) {
5575 if (from->east_west > to->east_west)
5576 coord->east_west = from->east_west - dist->lon;
5578 coord->east_west = from->east_west + dist->lon;
5579 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5581 if (from->north_south > to->north_south)
5582 coord->north_south = from->north_south - dist->lat;
5584 coord->north_south = from->north_south + dist->lat;
5585 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5591 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5593 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5594 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5596 VikCoord *next = from;
5598 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5600 list = g_list_prepend(list, next);
5606 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5608 typedef struct _Rect {
5613 #define GLRECT(iter) ((Rect *)((iter)->data))
5616 GList *rects_to_download = NULL;
5619 if (get_download_area_width(vvp, zoom_level, &wh))
5622 GList *iter = tr->trackpoints;
5626 gboolean new_map = TRUE;
5627 VikCoord *cur_coord, tl, br;
5630 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5632 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5633 rect = g_malloc(sizeof(Rect));
5636 rect->center = *cur_coord;
5637 rects_to_download = g_list_prepend(rects_to_download, rect);
5642 gboolean found = FALSE;
5643 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5644 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
5655 GList *fillins = NULL;
5656 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
5657 /* seems that ATM the function get_next_coord works only for LATLON */
5658 if ( cur_coord->mode == VIK_COORD_LATLON ) {
5659 /* fill-ins for far apart points */
5660 GList *cur_rect, *next_rect;
5661 for (cur_rect = rects_to_download;
5662 (next_rect = cur_rect->next) != NULL;
5663 cur_rect = cur_rect->next) {
5664 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
5665 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
5666 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
5670 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
5673 GList *iter = fillins;
5675 cur_coord = (VikCoord *)(iter->data);
5676 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5677 rect = g_malloc(sizeof(Rect));
5680 rect->center = *cur_coord;
5681 rects_to_download = g_list_prepend(rects_to_download, rect);
5686 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5687 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
5691 for (iter = fillins; iter; iter = iter->next)
5693 g_list_free(fillins);
5695 if (rects_to_download) {
5696 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
5697 g_free(rect_iter->data);
5698 g_list_free(rects_to_download);
5702 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
5705 gint selected_map, default_map;
5706 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
5707 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
5708 gint selected_zoom, default_zoom;
5712 VikTrwLayer *vtl = pass_along[0];
5713 VikLayersPanel *vlp = pass_along[1];
5714 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5715 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
5717 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
5718 int num_maps = g_list_length(vmls);
5721 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
5725 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
5726 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
5728 gchar **np = map_names;
5729 VikMapsLayer **lp = map_layers;
5730 for (i = 0; i < num_maps; i++) {
5731 gboolean dup = FALSE;
5732 vml = (VikMapsLayer *)(vmls->data);
5733 for (j = 0; j < i; j++) { /* no duplicate allowed */
5734 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
5741 *np++ = vik_maps_layer_get_map_label(vml);
5747 num_maps = lp - map_layers;
5749 for (default_map = 0; default_map < num_maps; default_map++) {
5750 /* TODO: check for parent layer's visibility */
5751 if (VIK_LAYER(map_layers[default_map])->visible)
5754 default_map = (default_map == num_maps) ? 0 : default_map;
5756 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
5757 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
5758 if (cur_zoom == zoom_vals[default_zoom])
5761 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
5763 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
5766 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
5769 for (i = 0; i < num_maps; i++)
5770 g_free(map_names[i]);
5778 /**** lowest waypoint number calculation ***/
5779 static gint highest_wp_number_name_to_number(const gchar *name) {
5780 if ( strlen(name) == 3 ) {
5782 if ( n < 100 && name[0] != '0' )
5784 if ( n < 10 && name[0] != '0' )
5792 static void highest_wp_number_reset(VikTrwLayer *vtl)
5794 vtl->highest_wp_number = -1;
5797 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
5799 /* if is bigger that top, add it */
5800 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
5801 if ( new_wp_num > vtl->highest_wp_number )
5802 vtl->highest_wp_number = new_wp_num;
5805 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
5807 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
5808 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
5809 if ( vtl->highest_wp_number == old_wp_num ) {
5811 vtl->highest_wp_number --;
5813 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5814 /* search down until we find something that *does* exist */
5816 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
5817 vtl->highest_wp_number --;
5818 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5823 /* get lowest unused number */
5824 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
5827 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
5829 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
5830 return g_strdup(buf);