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_OPENSTREETMAP
258 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
260 #ifdef VIK_CONFIG_GEOCACHES
261 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
265 static void trw_layer_properties_item ( gpointer pass_along[6] );
266 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
267 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
269 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
270 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
271 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
273 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
274 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
275 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
276 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
277 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
279 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
280 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
281 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
282 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
283 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
284 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
285 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
286 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
287 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
288 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
289 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
290 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
291 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
292 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
293 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
294 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
295 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
296 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
297 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
298 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
301 static void cached_pixbuf_free ( CachedPixbuf *cp );
302 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
304 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
305 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
307 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
308 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
309 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
311 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
312 static void highest_wp_number_reset(VikTrwLayer *vtl);
313 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
314 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
317 static VikToolInterface trw_layer_tools[] = {
318 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
319 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
321 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
322 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
323 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
325 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
326 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
328 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
329 (VikToolMouseFunc) tool_edit_waypoint_click,
330 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
331 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
333 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
334 (VikToolMouseFunc) tool_edit_trackpoint_click,
335 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
336 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
338 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
339 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
341 { N_("Route Finder"), (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
342 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
344 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
346 /****** PARAMETERS ******/
348 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
349 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
351 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
352 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
355 static VikLayerParamScale params_scales[] = {
356 /* min max step digits */
357 { 1, 10, 1, 0 }, /* line_thickness */
358 { 0.0, 99.0, 1, 2 }, /* velocity_min */
359 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
360 /* 5 * step == how much to turn */
361 { 16, 128, 3.2, 0 }, /* image_size */
362 { 0, 255, 5, 0 }, /* image alpha */
363 { 5, 500, 5, 0 }, /* image cache_size */
364 { 0, 8, 1, 0 }, /* image cache_size */
365 { 1, 64, 1, 0 }, /* wpsize */
366 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
367 { 1, 100, 1, 0 }, /* stop_length */
370 VikLayerParam trw_layer_params[] = {
371 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
372 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
374 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
375 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
376 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
377 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
378 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
380 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
381 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
383 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
384 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
385 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
386 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
387 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
389 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
390 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
391 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
392 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
393 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
394 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
395 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
396 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
398 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
399 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
400 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
401 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
404 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 };
407 *** 1) Add to trw_layer_params and enumeration
408 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
411 /****** END PARAMETERS ******/
413 static VikTrwLayer* trw_layer_new ( gint drawmode );
414 /* Layer Interface function definitions */
415 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
416 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
417 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
418 static void trw_layer_free ( VikTrwLayer *trwlayer );
419 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
420 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
421 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
422 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
423 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
424 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
425 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
426 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
427 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
428 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
429 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
430 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
431 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
432 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
433 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
434 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
435 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
436 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
437 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
438 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
439 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
440 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
441 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
442 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
443 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
444 /* End Layer Interface function definitions */
446 VikLayerInterface vik_trw_layer_interface = {
451 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
455 params_groups, /* params_groups */
456 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
460 (VikLayerFuncCreate) trw_layer_create,
461 (VikLayerFuncRealize) trw_layer_realize,
462 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
463 (VikLayerFuncFree) trw_layer_free,
465 (VikLayerFuncProperties) NULL,
466 (VikLayerFuncDraw) trw_layer_draw,
467 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
469 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
470 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
472 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
473 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
475 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
476 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
477 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
478 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
479 (VikLayerFuncLayerSelected) trw_layer_selected,
481 (VikLayerFuncMarshall) trw_layer_marshall,
482 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
484 (VikLayerFuncSetParam) trw_layer_set_param,
485 (VikLayerFuncGetParam) trw_layer_get_param,
487 (VikLayerFuncReadFileData) a_gpspoint_read_file,
488 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
490 (VikLayerFuncDeleteItem) trw_layer_del_item,
491 (VikLayerFuncCutItem) trw_layer_cut_item,
492 (VikLayerFuncCopyItem) trw_layer_copy_item,
493 (VikLayerFuncPasteItem) trw_layer_paste_item,
494 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
496 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
498 (VikLayerFuncSelectClick) trw_layer_select_click,
499 (VikLayerFuncSelectMove) trw_layer_select_move,
500 (VikLayerFuncSelectRelease) trw_layer_select_release,
501 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
504 /* for copy & paste (I think?) */
512 GType vik_trw_layer_get_type ()
514 static GType vtl_type = 0;
518 static const GTypeInfo vtl_info =
520 sizeof (VikTrwLayerClass),
521 NULL, /* base_init */
522 NULL, /* base_finalize */
523 NULL, /* class init */
524 NULL, /* class_finalize */
525 NULL, /* class_data */
526 sizeof (VikTrwLayer),
528 NULL /* instance init */
530 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
536 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
538 static gpointer pass_along[6];
544 pass_along[1] = NULL;
545 pass_along[2] = GINT_TO_POINTER (subtype);
546 pass_along[3] = sublayer;
547 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
548 pass_along[5] = NULL;
550 trw_layer_delete_item ( pass_along );
553 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
555 static gpointer pass_along[6];
561 pass_along[1] = NULL;
562 pass_along[2] = GINT_TO_POINTER (subtype);
563 pass_along[3] = sublayer;
564 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
565 pass_along[5] = NULL;
567 trw_layer_copy_item_cb(pass_along);
568 trw_layer_cut_item_cb(pass_along);
571 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
573 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
574 gint subtype = GPOINTER_TO_INT (pass_along[2]);
575 gpointer * sublayer = pass_along[3];
579 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
582 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
583 subtype, len, (const gchar*) sublayer, data);
587 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
589 trw_layer_copy_item_cb(pass_along);
590 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
591 trw_layer_delete_item(pass_along);
594 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
605 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
607 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
609 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
612 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
613 fi = g_malloc ( *len );
614 fi->len = strlen(sublayer) + 1;
615 memcpy(fi->data, sublayer, fi->len);
616 memcpy(fi->data + fi->len, id, il);
618 *item = (guint8 *)fi;
621 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
623 FlatItem *fi = (FlatItem *) item;
625 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
630 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
631 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
632 vik_trw_layer_add_waypoint ( vtl, name, w );
633 waypoint_convert(name, w, &vtl->coord_mode);
634 // Consider if redraw necessary for the new item
635 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
636 vik_layer_emit_update ( VIK_LAYER(vtl) );
639 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
643 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
644 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
645 vik_trw_layer_add_track ( vtl, name, t );
646 track_convert(name, t, &vtl->coord_mode);
647 // Consider if redraw necessary for the new item
648 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
649 vik_layer_emit_update ( VIK_LAYER(vtl) );
655 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
662 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
666 case PARAM_TV: vtl->tracks_visible = data.b; break;
667 case PARAM_WV: vtl->waypoints_visible = data.b; break;
668 case PARAM_DM: vtl->drawmode = data.u; break;
669 case PARAM_DP: vtl->drawpoints = data.b; break;
670 case PARAM_DE: vtl->drawelevation = data.b; break;
671 case PARAM_DS: vtl->drawstops = data.b; break;
672 case PARAM_DL: vtl->drawlines = data.b; break;
673 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
674 vtl->stop_length = data.u;
676 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
677 vtl->elevation_factor = data.u;
679 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
681 vtl->line_thickness = data.u;
682 trw_layer_new_track_gcs ( vtl, vp );
685 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
687 vtl->bg_line_thickness = data.u;
688 trw_layer_new_track_gcs ( vtl, vp );
693 /* Convert to store internally
694 NB file operation always in internal units (metres per second) */
695 vik_units_speed_t speed_units = a_vik_get_units_speed ();
696 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
697 vtl->velocity_min = data.d;
698 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
699 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
700 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
701 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
704 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
709 /* Convert to store internally
710 NB file operation always in internal units (metres per second) */
711 vik_units_speed_t speed_units = a_vik_get_units_speed ();
712 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
713 vtl->velocity_max = data.d;
714 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
715 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
716 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
717 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
720 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
723 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
724 case PARAM_DLA: vtl->drawlabels = data.b; break;
725 case PARAM_DI: vtl->drawimages = data.b; break;
726 case PARAM_IS: if ( data.u != vtl->image_size )
728 vtl->image_size = data.u;
729 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
730 g_queue_free ( vtl->image_cache );
731 vtl->image_cache = g_queue_new ();
734 case PARAM_IA: vtl->image_alpha = data.u; break;
735 case PARAM_ICS: vtl->image_cache_size = data.u;
736 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
737 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
739 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
740 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
741 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
742 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
743 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
744 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
745 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
750 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
752 VikLayerParamData rv;
755 case PARAM_TV: rv.b = vtl->tracks_visible; break;
756 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
757 case PARAM_DM: rv.u = vtl->drawmode; break;
758 case PARAM_DP: rv.b = vtl->drawpoints; break;
759 case PARAM_DE: rv.b = vtl->drawelevation; break;
760 case PARAM_EF: rv.u = vtl->elevation_factor; break;
761 case PARAM_DS: rv.b = vtl->drawstops; break;
762 case PARAM_SL: rv.u = vtl->stop_length; break;
763 case PARAM_DL: rv.b = vtl->drawlines; break;
764 case PARAM_LT: rv.u = vtl->line_thickness; break;
765 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
768 /* Convert to store internally
769 NB file operation always in internal units (metres per second) */
770 vik_units_speed_t speed_units = a_vik_get_units_speed ();
771 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
772 rv.d = vtl->velocity_min;
773 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
774 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
775 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
776 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
779 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
784 /* Convert to store internally
785 NB file operation always in internal units (metres per second) */
786 vik_units_speed_t speed_units = a_vik_get_units_speed ();
787 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
788 rv.d = vtl->velocity_max;
789 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
790 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
791 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
792 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
795 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
798 case PARAM_DLA: rv.b = vtl->drawlabels; break;
799 case PARAM_DI: rv.b = vtl->drawimages; break;
800 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
801 case PARAM_IS: rv.u = vtl->image_size; break;
802 case PARAM_IA: rv.u = vtl->image_alpha; break;
803 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
804 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
805 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
806 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
807 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
808 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
809 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
810 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
815 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
826 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
827 a_gpx_write_file(vtl, f);
828 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
831 g_file_get_contents(tmpname, &dd, &dl, NULL);
832 *len = sizeof(pl) + pl + dl;
833 *data = g_malloc(*len);
834 memcpy(*data, &pl, sizeof(pl));
835 memcpy(*data + sizeof(pl), pd, pl);
836 memcpy(*data + sizeof(pl) + pl, dd, dl);
845 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
847 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
853 memcpy(&pl, data, sizeof(pl));
855 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
858 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
859 g_critical("couldn't open temp file");
862 fwrite(data, len - pl - sizeof(pl), 1, f);
864 a_gpx_read_file(rv, f);
872 static GList * str_array_to_glist(gchar* data[])
876 for (p = (gpointer)data; *p; p++)
877 gl = g_list_prepend(gl, *p);
878 return(g_list_reverse(gl));
881 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
883 return (strcasecmp(s1, s2) == 0);
886 static guint strcase_hash(gconstpointer v)
888 /* 31 bit hash function */
891 gchar s[128]; /* malloc is too slow for reading big files */
894 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
895 p[i] = toupper(t[i]);
901 for (p += 1; *p != '\0'; p++)
902 h = (h << 5) - h + *p;
908 static VikTrwLayer* trw_layer_new ( gint drawmode )
910 if (trw_layer_params[PARAM_DM].widget_data == NULL)
911 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
912 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
913 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
915 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
916 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
918 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
919 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
920 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
921 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
923 /* TODO: constants at top */
924 rv->waypoints_visible = rv->tracks_visible = TRUE;
925 rv->drawmode = drawmode;
926 rv->drawpoints = TRUE;
927 rv->drawstops = FALSE;
928 rv->drawelevation = FALSE;
929 rv->elevation_factor = 30;
930 rv->stop_length = 60;
931 rv->drawlines = TRUE;
932 rv->wplabellayout = NULL;
933 rv->wp_right_click_menu = NULL;
934 rv->track_right_click_menu = NULL;
935 rv->waypoint_gc = NULL;
936 rv->waypoint_text_gc = NULL;
937 rv->waypoint_bg_gc = NULL;
939 rv->velocity_max = 5.0;
940 rv->velocity_min = 0.0;
941 rv->line_thickness = 1;
942 rv->bg_line_thickness = 0;
943 rv->current_wp = NULL;
944 rv->current_wp_name = NULL;
945 rv->current_track = NULL;
946 rv->current_tpl = NULL;
947 rv->current_tp_track_name = NULL;
948 rv->moving_tp = FALSE;
949 rv->moving_wp = FALSE;
951 rv->ct_sync_done = TRUE;
953 rv->route_finder_started = FALSE;
954 rv->route_finder_check_added_track = FALSE;
955 rv->route_finder_added_track_name = NULL;
956 rv->route_finder_current_track = NULL;
957 rv->route_finder_append = FALSE;
959 rv->waypoint_rightclick = FALSE;
961 rv->last_tp_track_name = NULL;
963 rv->image_cache = g_queue_new();
965 rv->image_alpha = 255;
966 rv->image_cache_size = 300;
967 rv->drawimages = TRUE;
968 rv->drawlabels = TRUE;
973 static void trw_layer_free ( VikTrwLayer *trwlayer )
975 g_hash_table_destroy(trwlayer->waypoints);
976 g_hash_table_destroy(trwlayer->tracks);
978 /* ODC: replace with GArray */
979 trw_layer_free_track_gcs ( trwlayer );
981 if ( trwlayer->wp_right_click_menu )
982 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
984 if ( trwlayer->track_right_click_menu )
985 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
987 if ( trwlayer->wplabellayout != NULL)
988 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
990 if ( trwlayer->waypoint_gc != NULL )
991 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
993 if ( trwlayer->waypoint_text_gc != NULL )
994 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
996 if ( trwlayer->waypoint_bg_gc != NULL )
997 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
999 if ( trwlayer->waypoint_font != NULL )
1000 gdk_font_unref ( trwlayer->waypoint_font );
1002 if ( trwlayer->tpwin != NULL )
1003 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1005 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1006 g_queue_free ( trwlayer->image_cache );
1009 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
1012 dp->xmpp = vik_viewport_get_xmpp ( vp );
1013 dp->ympp = vik_viewport_get_ympp ( vp );
1014 dp->width = vik_viewport_get_width ( vp );
1015 dp->height = vik_viewport_get_height ( vp );
1016 dp->center = vik_viewport_get_center ( vp );
1017 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1018 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1023 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1024 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1025 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1027 dp->ce1 = dp->center->east_west-w2;
1028 dp->ce2 = dp->center->east_west+w2;
1029 dp->cn1 = dp->center->north_south-h2;
1030 dp->cn2 = dp->center->north_south+h2;
1031 } else if ( dp->lat_lon ) {
1032 VikCoord upperleft, bottomright;
1033 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1034 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1035 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1036 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1037 dp->ce1 = upperleft.east_west;
1038 dp->ce2 = bottomright.east_west;
1039 dp->cn1 = bottomright.north_south;
1040 dp->cn2 = upperleft.north_south;
1043 dp->track_gc_iter = 0;
1046 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1048 static gdouble rv = 0;
1049 if ( tp1->has_timestamp && tp2->has_timestamp )
1051 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1052 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1055 return VIK_TRW_LAYER_TRACK_GC_MIN;
1056 else if ( vtl->velocity_min >= vtl->velocity_max )
1057 return VIK_TRW_LAYER_TRACK_GC_MAX;
1059 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1061 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1062 return VIK_TRW_LAYER_TRACK_GC_MAX;
1066 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1069 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1071 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1072 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1073 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1074 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1077 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1079 /* TODO: this function is a mess, get rid of any redundancy */
1080 GList *list = track->trackpoints;
1082 gboolean useoldvals = TRUE;
1084 gboolean drawpoints;
1086 gboolean drawelevation;
1087 gdouble min_alt, max_alt, alt_diff = 0;
1089 const guint8 tp_size_reg = 2;
1090 const guint8 tp_size_cur = 4;
1093 if ( dp->vtl->drawelevation )
1095 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1096 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1097 alt_diff = max_alt - min_alt;
1100 if ( ! track->visible )
1103 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1104 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1105 trw_layer_draw_track ( name, track, dp, TRUE );
1107 if ( drawing_white_background )
1108 drawpoints = drawstops = FALSE;
1110 drawpoints = dp->vtl->drawpoints;
1111 drawstops = dp->vtl->drawstops;
1114 /* Current track - used for creation */
1115 if ( track == dp->vtl->current_track )
1116 main_gc = dp->vtl->current_track_gc;
1118 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1119 /* Draw all tracks of the layer in special colour */
1120 /* if track is member of selected layer or is the current selected track
1121 then draw in the highlight colour.
1122 NB this supercedes the drawmode */
1123 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1124 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1125 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1126 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1129 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1130 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1132 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1136 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1137 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1139 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1144 int x, y, oldx, oldy;
1145 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1147 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1149 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1151 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1153 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1154 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1160 while ((list = g_list_next(list)))
1162 tp = VIK_TRACKPOINT(list->data);
1163 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1165 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1166 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1167 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1168 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1169 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1171 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1173 if ( drawpoints && ! drawing_white_background )
1176 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1179 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1180 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 );
1183 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 );
1186 if ((!tp->newsegment) && (dp->vtl->drawlines))
1188 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1190 /* UTM only: zone check */
1191 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1192 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1194 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1195 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1196 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1200 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1202 if ( drawing_white_background ) {
1203 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1207 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1208 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1210 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1211 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1215 tmp[1].y = oldy-FIXALTITUDE(list->data);
1217 tmp[2].y = y-FIXALTITUDE(list->next->data);
1222 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1223 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1225 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1226 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1228 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1238 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1240 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1241 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1243 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1244 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1245 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1246 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1249 if ( drawing_white_background )
1250 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1252 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1256 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1257 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1264 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1265 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1266 dp->track_gc_iter = 0;
1269 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1270 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1272 trw_layer_draw_track ( name, track, dp, FALSE );
1275 static void cached_pixbuf_free ( CachedPixbuf *cp )
1277 g_object_unref ( G_OBJECT(cp->pixbuf) );
1278 g_free ( cp->image );
1281 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1283 return strcmp ( cp->image, name );
1286 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1289 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1290 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1291 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1294 GdkPixbuf *sym = NULL;
1295 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1297 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1299 if ( wp->image && dp->vtl->drawimages )
1301 GdkPixbuf *pixbuf = NULL;
1304 if ( dp->vtl->image_alpha == 0)
1307 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1309 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1312 gchar *image = wp->image;
1313 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1314 if ( ! regularthumb )
1316 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1317 image = "\x12\x00"; /* this shouldn't occur naturally. */
1321 CachedPixbuf *cp = NULL;
1322 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1323 if ( dp->vtl->image_size == 128 )
1324 cp->pixbuf = regularthumb;
1327 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1328 g_assert ( cp->pixbuf );
1329 g_object_unref ( G_OBJECT(regularthumb) );
1331 cp->image = g_strdup ( image );
1333 /* needed so 'click picture' tool knows how big the pic is; we don't
1334 * store it in cp because they may have been freed already. */
1335 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1336 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1338 g_queue_push_head ( dp->vtl->image_cache, cp );
1339 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1340 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1342 pixbuf = cp->pixbuf;
1346 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1352 w = gdk_pixbuf_get_width ( pixbuf );
1353 h = gdk_pixbuf_get_height ( pixbuf );
1355 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1357 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1358 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1359 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1360 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1361 // Highlighted - so draw a little border around the chosen one
1362 // single line seems a little weak so draw 2 of them
1363 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1364 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1365 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1366 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1369 if ( dp->vtl->image_alpha == 255 )
1370 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1372 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1374 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1378 /* DRAW ACTUAL DOT */
1379 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1380 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 );
1382 else if ( wp == dp->vtl->current_wp ) {
1383 switch ( dp->vtl->wp_symbol ) {
1384 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;
1385 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;
1386 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;
1387 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 );
1388 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 );
1392 switch ( dp->vtl->wp_symbol ) {
1393 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;
1394 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;
1395 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;
1396 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 );
1397 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;
1401 if ( dp->vtl->drawlabels )
1403 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1404 gint label_x, label_y;
1406 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1407 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1408 label_x = x - width/2;
1410 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1412 label_y = y - dp->vtl->wp_size - height - 2;
1414 /* if highlight mode on, then draw background text in highlight colour */
1415 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1416 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1417 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1418 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1419 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), 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);
1424 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1426 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1431 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1433 static struct DrawingParams dp;
1434 g_assert ( l != NULL );
1436 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1439 if ( l->tracks_visible )
1440 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1442 if (l->waypoints_visible)
1443 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1446 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1449 if ( vtl->track_bg_gc )
1451 g_object_unref ( vtl->track_bg_gc );
1452 vtl->track_bg_gc = NULL;
1454 if ( vtl->current_track_gc )
1456 g_object_unref ( vtl->current_track_gc );
1457 vtl->current_track_gc = NULL;
1460 if ( ! vtl->track_gc )
1462 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1463 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1464 g_array_free ( vtl->track_gc, TRUE );
1465 vtl->track_gc = NULL;
1468 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1470 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1471 gint width = vtl->line_thickness;
1473 if ( vtl->track_gc )
1474 trw_layer_free_track_gcs ( vtl );
1476 if ( vtl->track_bg_gc )
1477 g_object_unref ( vtl->track_bg_gc );
1478 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1480 if ( vtl->current_track_gc )
1481 g_object_unref ( vtl->current_track_gc );
1482 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1483 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1485 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1487 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1489 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1490 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1491 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1492 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1493 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1494 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1495 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1496 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1497 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1498 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1500 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1502 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1504 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1507 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1509 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1510 PangoFontDescription *pfd;
1511 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1512 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1513 pango_layout_set_font_description (rv->wplabellayout, pfd);
1514 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1515 pango_font_description_free (pfd);
1517 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1519 trw_layer_new_track_gcs ( rv, vp );
1521 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1522 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1523 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1524 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1526 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1528 rv->has_verified_thumbnails = FALSE;
1529 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1531 rv->wp_draw_symbols = TRUE;
1533 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1535 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1540 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1542 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1544 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1545 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 );
1547 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 );
1550 *new_iter = *((GtkTreeIter *) pass_along[1]);
1551 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1553 if ( ! track->visible )
1554 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1557 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1559 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1560 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1561 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 );
1563 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 );
1566 *new_iter = *((GtkTreeIter *) pass_along[1]);
1567 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1569 if ( ! wp->visible )
1570 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1574 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1577 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1579 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1580 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1582 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1584 if ( ! vtl->tracks_visible )
1585 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1587 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1589 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1590 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1592 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1595 if ( ! vtl->waypoints_visible )
1596 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1598 pass_along[0] = &(vtl->waypoints_iter);
1599 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1601 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1605 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1609 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1610 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1611 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1613 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1615 return (t->visible ^= 1);
1619 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1621 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1623 return (t->visible ^= 1);
1632 * Return a property about tracks for this layer
1634 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1636 return vtl->line_thickness;
1639 // Structure to hold multiple track information for a layer
1648 * Build up layer multiple track information via updating the tooltip_tracks structure
1650 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1652 tt->length = tt->length + vik_track_get_length (tr);
1654 // Ensure times are available
1655 if ( tr->trackpoints &&
1656 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1657 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1660 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1661 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1663 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1664 // Hence initialize to the first 'proper' value
1665 if ( tt->start_time == 0 )
1666 tt->start_time = t1;
1667 if ( tt->end_time == 0 )
1670 // Update find the earliest / last times
1671 if ( t1 < tt->start_time )
1672 tt->start_time = t1;
1673 if ( t2 > tt->end_time )
1676 // Keep track of total time
1677 // there maybe gaps within a track (eg segments)
1678 // but this should be generally good enough for a simple indicator
1679 tt->duration = tt->duration + (int)(t2-t1);
1684 * Generate tooltip text for the layer.
1685 * This is relatively complicated as it considers information for
1686 * no tracks, a single track or multiple tracks
1687 * (which may or may not have timing information)
1689 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1700 static gchar tmp_buf[128];
1703 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1705 // Safety check - I think these should always be valid
1706 if ( vtl->tracks && vtl->waypoints ) {
1707 tooltip_tracks tt = { 0.0, 0, 0 };
1708 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1710 GDate* gdate_start = g_date_new ();
1711 g_date_set_time_t (gdate_start, tt.start_time);
1713 GDate* gdate_end = g_date_new ();
1714 g_date_set_time_t (gdate_end, tt.end_time);
1716 if ( g_date_compare (gdate_start, gdate_end) ) {
1717 // Dates differ so print range on separate line
1718 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1719 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1720 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1723 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1724 if ( tt.start_time != 0 )
1725 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1729 if ( tt.length > 0.0 ) {
1730 gdouble len_in_units;
1732 // Setup info dependent on distance units
1733 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1734 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1735 len_in_units = VIK_METERS_TO_MILES(tt.length);
1738 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1739 len_in_units = tt.length/1000.0;
1742 // Timing information if available
1744 if ( tt.duration > 0 ) {
1745 g_snprintf (tbuf1, sizeof(tbuf1),
1746 _(" in %d:%02d hrs:mins"),
1747 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1749 g_snprintf (tbuf2, sizeof(tbuf2),
1750 _("\n%sTotal Length %.1f %s%s"),
1751 tbuf3, len_in_units, tbuf4, tbuf1);
1754 // Put together all the elements to form compact tooltip text
1755 g_snprintf (tmp_buf, sizeof(tmp_buf),
1756 _("Tracks: %d - Waypoints: %d%s"),
1757 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1759 g_date_free (gdate_start);
1760 g_date_free (gdate_end);
1767 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1771 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1772 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1773 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1775 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1777 // Could be a better way of handling strings - but this works...
1778 gchar time_buf1[20];
1779 gchar time_buf2[20];
1780 time_buf1[0] = '\0';
1781 time_buf2[0] = '\0';
1782 static gchar tmp_buf[100];
1783 // Compact info: Short date eg (11/20/99), duration and length
1784 // Hopefully these are the things that are most useful and so promoted into the tooltip
1785 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1786 // %x The preferred date representation for the current locale without the time.
1787 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1788 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1789 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1791 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1794 // Get length and consider the appropriate distance units
1795 gdouble tr_len = vik_track_get_length(tr);
1796 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1797 switch (dist_units) {
1798 case VIK_UNITS_DISTANCE_KILOMETRES:
1799 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1801 case VIK_UNITS_DISTANCE_MILES:
1802 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1811 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1813 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1814 // NB It's OK to return NULL
1824 * General layer selection function, find out which bit is selected and take appropriate action
1826 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1829 l->current_wp = NULL;
1830 l->current_wp_name = NULL;
1831 trw_layer_cancel_current_tp ( l, FALSE );
1835 case VIK_TREEVIEW_TYPE_LAYER:
1837 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1838 /* Mark for redraw */
1843 case VIK_TREEVIEW_TYPE_SUBLAYER:
1847 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1849 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1850 /* Mark for redraw */
1854 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1856 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->tracks, sublayer ), l, sublayer );
1857 /* Mark for redraw */
1861 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1863 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
1864 /* Mark for redraw */
1868 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1870 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->waypoints, sublayer ), l, sublayer );
1871 /* Mark for redraw */
1877 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1886 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1891 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1896 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1898 return l->waypoints;
1901 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1903 static VikCoord fixme;
1904 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1905 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1906 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1907 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1908 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1909 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1910 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1911 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1912 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1915 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1918 static VikCoord fixme;
1922 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1923 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1924 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1925 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1926 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1927 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1928 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1929 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1930 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1935 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1937 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1938 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1940 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1941 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1942 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1943 maxmin[0].lat = wpt_maxmin[0].lat;
1946 maxmin[0].lat = trk_maxmin[0].lat;
1948 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1949 maxmin[0].lon = wpt_maxmin[0].lon;
1952 maxmin[0].lon = trk_maxmin[0].lon;
1954 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1955 maxmin[1].lat = wpt_maxmin[1].lat;
1958 maxmin[1].lat = trk_maxmin[1].lat;
1960 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1961 maxmin[1].lon = wpt_maxmin[1].lon;
1964 maxmin[1].lon = trk_maxmin[1].lon;
1968 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1970 /* 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... */
1971 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1972 trw_layer_find_maxmin (vtl, maxmin);
1973 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1977 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1978 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1983 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1986 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1987 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
1989 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1992 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1994 /* First set the center [in case previously viewing from elsewhere] */
1995 /* Then loop through zoom levels until provided positions are in view */
1996 /* This method is not particularly fast - but should work well enough */
1997 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1999 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2000 vik_viewport_set_center_coord ( vvp, &coord );
2002 /* Convert into definite 'smallest' and 'largest' positions */
2003 struct LatLon minmin;
2004 if ( maxmin[0].lat < maxmin[1].lat )
2005 minmin.lat = maxmin[0].lat;
2007 minmin.lat = maxmin[1].lat;
2009 struct LatLon maxmax;
2010 if ( maxmin[0].lon > maxmin[1].lon )
2011 maxmax.lon = maxmin[0].lon;
2013 maxmax.lon = maxmin[1].lon;
2015 /* Never zoom in too far - generally not that useful, as too close ! */
2016 /* Always recalculate the 'best' zoom level */
2018 vik_viewport_set_zoom ( vvp, zoom );
2020 gdouble min_lat, max_lat, min_lon, max_lon;
2021 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2022 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2023 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2024 /* NB I think the logic used in this test to determine if the bounds is within view
2025 fails if track goes across 180 degrees longitude.
2026 Hopefully that situation is not too common...
2027 Mind you viking doesn't really do edge locations to well anyway */
2028 if ( min_lat < minmin.lat &&
2029 max_lat > minmin.lat &&
2030 min_lon < maxmax.lon &&
2031 max_lon > maxmax.lon )
2032 /* Found within zoom level */
2037 vik_viewport_set_zoom ( vvp, zoom );
2041 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2043 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2044 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2045 trw_layer_find_maxmin (vtl, maxmin);
2046 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2049 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2054 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2056 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])) ) ) {
2057 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2060 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2063 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2065 GtkWidget *file_selector;
2067 gboolean failed = FALSE;
2068 file_selector = gtk_file_chooser_dialog_new (title,
2070 GTK_FILE_CHOOSER_ACTION_SAVE,
2071 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2072 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2074 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2076 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2078 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2079 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2081 gtk_widget_hide ( file_selector );
2082 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2087 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2089 gtk_widget_hide ( file_selector );
2090 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2095 gtk_widget_destroy ( file_selector );
2097 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2100 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2102 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2105 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2107 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2110 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2112 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2113 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2114 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2115 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2117 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2119 g_free ( auto_save_name );
2122 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2124 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2125 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2126 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2127 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2129 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2131 g_free ( auto_save_name );
2134 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2136 gpointer layer_and_vlp[2];
2137 layer_and_vlp[0] = pass_along[0];
2138 layer_and_vlp[1] = pass_along[1];
2140 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2141 gchar *auto_save_name = g_strdup ( pass_along[3] );
2142 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2143 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2145 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2147 g_free ( auto_save_name );
2150 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2152 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2153 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2154 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2155 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2157 GTK_RESPONSE_REJECT,
2159 GTK_RESPONSE_ACCEPT,
2162 GtkWidget *label, *entry;
2163 label = gtk_label_new(_("Waypoint Name:"));
2164 entry = gtk_entry_new();
2166 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2167 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2168 gtk_widget_show_all ( label );
2169 gtk_widget_show_all ( entry );
2171 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2173 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2176 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2179 for ( i = strlen(upname)-1; i >= 0; i-- )
2180 upname[i] = toupper(upname[i]);
2182 wp = g_hash_table_lookup ( wps, upname );
2185 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2188 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2189 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2190 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 );
2197 gtk_widget_destroy ( dia );
2200 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2202 gchar *default_name = highest_wp_number_get(vtl);
2203 VikWaypoint *wp = vik_waypoint_new();
2204 gchar *returned_name;
2206 wp->coord = *def_coord;
2208 // Attempt to auto set height if DEM data is available
2209 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2210 if ( elev != VIK_DEM_INVALID_ELEVATION )
2211 wp->altitude = (gdouble)elev;
2213 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2216 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2217 g_free (default_name);
2220 g_free (default_name);
2221 vik_waypoint_free(wp);
2225 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2228 struct LatLon one_ll, two_ll;
2229 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2231 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2232 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2233 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2234 VikViewport *vvp = vik_window_viewport(vw);
2235 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2236 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2237 vik_coord_to_latlon(&one, &one_ll);
2238 vik_coord_to_latlon(&two, &two_ll);
2239 if (one_ll.lat > two_ll.lat) {
2240 maxmin[0].lat = one_ll.lat;
2241 maxmin[1].lat = two_ll.lat;
2244 maxmin[0].lat = two_ll.lat;
2245 maxmin[1].lat = one_ll.lat;
2247 if (one_ll.lon > two_ll.lon) {
2248 maxmin[0].lon = one_ll.lon;
2249 maxmin[1].lon = two_ll.lon;
2252 maxmin[0].lon = two_ll.lon;
2253 maxmin[1].lon = one_ll.lon;
2255 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2258 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2260 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2261 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2262 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2264 trw_layer_find_maxmin (vtl, maxmin);
2265 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2268 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2271 * Acquire into this TRW Layer straight from GPS Device
2273 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2275 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2276 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2277 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2278 VikViewport *vvp = vik_window_viewport(vw);
2280 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2281 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2285 * Acquire into this TRW Layer from Google Directions
2287 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2289 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2290 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2291 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2292 VikViewport *vvp = vik_window_viewport(vw);
2294 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2297 #ifdef VIK_CONFIG_OPENSTREETMAP
2299 * Acquire into this TRW Layer from OSM
2301 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2303 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2304 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2305 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2306 VikViewport *vvp = vik_window_viewport(vw);
2308 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2312 #ifdef VIK_CONFIG_GEOCACHES
2314 * Acquire into this TRW Layer from Geocaching.com
2316 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2318 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2319 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2320 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2321 VikViewport *vvp = vik_window_viewport(vw);
2323 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2327 static void trw_layer_new_wp ( gpointer lav[2] )
2329 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2330 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2331 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2332 instead return true if you want to update. */
2333 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 )
2334 vik_layers_panel_emit_update ( vlp );
2337 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2339 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2340 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2342 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2343 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2344 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2345 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2346 vik_layers_panel_emit_update ( vlp );
2350 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2352 /* NB do not care if wp is visible or not */
2353 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2356 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2358 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2359 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2361 /* Only 1 waypoint - jump straight to it */
2362 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2363 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2364 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2366 /* If at least 2 waypoints - find center and then zoom to fit */
2367 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2369 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2370 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2371 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2374 vik_layers_panel_emit_update ( vlp );
2377 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2379 static gpointer pass_along[2];
2381 GtkWidget *export_submenu;
2382 GtkWidget *wikipedia_submenu;
2383 pass_along[0] = vtl;
2384 pass_along[1] = vlp;
2386 item = gtk_menu_item_new();
2387 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2388 gtk_widget_show ( item );
2390 /* Now with icons */
2391 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2392 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2394 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2395 gtk_widget_show ( item );
2397 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2398 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2399 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2400 gtk_widget_show ( item );
2402 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2403 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2404 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2405 gtk_widget_show ( item );
2407 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2408 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2409 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2410 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2411 gtk_widget_show ( item );
2413 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2414 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2415 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2416 gtk_widget_show ( item );
2418 export_submenu = gtk_menu_new ();
2419 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2420 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2421 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2422 gtk_widget_show ( item );
2423 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2425 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2426 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2427 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2428 gtk_widget_show ( item );
2430 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2431 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2432 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2433 gtk_widget_show ( item );
2435 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2436 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2437 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2438 gtk_widget_show ( item );
2440 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2442 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2443 gtk_widget_show ( item );
2445 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2446 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2448 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2449 gtk_widget_show ( item );
2451 #ifdef VIK_CONFIG_GEONAMES
2452 wikipedia_submenu = gtk_menu_new();
2453 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2454 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2455 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2456 gtk_widget_show(item);
2457 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2459 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2460 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2461 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2462 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2463 gtk_widget_show ( item );
2465 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2466 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2468 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2469 gtk_widget_show ( item );
2472 GtkWidget *acquire_submenu = gtk_menu_new ();
2473 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2474 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2475 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2476 gtk_widget_show ( item );
2477 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2479 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2480 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2481 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2482 gtk_widget_show ( item );
2484 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2485 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2486 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2487 gtk_widget_show ( item );
2489 #ifdef VIK_CONFIG_OPENSTREETMAP
2490 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2491 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2492 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2493 gtk_widget_show ( item );
2496 #ifdef VIK_CONFIG_GEOCACHES
2497 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2499 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2500 gtk_widget_show ( item );
2503 #ifdef VIK_CONFIG_OPENSTREETMAP
2504 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2507 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2508 gtk_widget_show ( item );
2511 GtkWidget *delete_submenu = gtk_menu_new ();
2512 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2513 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2514 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2515 gtk_widget_show ( item );
2516 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2518 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2519 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2520 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2521 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2522 gtk_widget_show ( item );
2524 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2525 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2526 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2527 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2528 gtk_widget_show ( item );
2530 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2531 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2532 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2533 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2534 gtk_widget_show ( item );
2536 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2537 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2538 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2539 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2540 gtk_widget_show ( item );
2542 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2543 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2545 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2546 gtk_widget_show ( item );
2549 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2550 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2552 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2553 gtk_widget_show ( item );
2557 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2559 if ( VIK_LAYER(vtl)->realized )
2561 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2563 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2566 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2567 // Visibility column always needed for waypoints
2568 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2569 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2571 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2573 // Actual setting of visibility dependent on the waypoint
2574 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2575 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2579 highest_wp_number_add_wp(vtl, name);
2580 g_hash_table_insert ( vtl->waypoints, name, wp );
2584 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2586 if ( VIK_LAYER(vtl)->realized )
2588 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2590 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2593 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2594 // Visibility column always needed for tracks
2595 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2596 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2598 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2600 // Actual setting of visibility dependent on the track
2601 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2602 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2606 g_hash_table_insert ( vtl->tracks, name, t );
2610 /* to be called whenever a track has been deleted or may have been changed. */
2611 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2613 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2614 trw_layer_cancel_current_tp ( vtl, FALSE );
2615 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2616 trw_layer_cancel_last_tp ( vtl );
2619 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2622 gchar *newname = g_strdup(name);
2623 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2624 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2625 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2627 newname = new_newname;
2633 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2635 vik_trw_layer_add_waypoint ( vtl,
2636 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2639 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2641 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
2642 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2643 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
2644 vik_track_free ( tr );
2645 vtl->route_finder_append = FALSE; /* this means we have added it */
2647 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2648 vik_trw_layer_add_track ( vtl, new_name, tr );
2650 if ( vtl->route_finder_check_added_track ) {
2651 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2652 if ( vtl->route_finder_added_track_name ) /* for google routes */
2653 g_free ( vtl->route_finder_added_track_name );
2654 vtl->route_finder_added_track_name = g_strdup(new_name);
2659 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2661 *l = g_list_append(*l, (gpointer)name);
2664 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2666 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2667 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2669 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2670 vik_trw_layer_delete_track(vtl_src, name);
2671 vik_trw_layer_add_track(vtl_dest, newname, t);
2673 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2675 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2676 vik_trw_layer_delete_waypoint(vtl_src, name);
2677 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2681 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2683 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2684 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2686 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2687 GList *items = NULL;
2690 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2691 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2693 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2694 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2699 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2700 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2702 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2709 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2710 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2714 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2716 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2717 gboolean was_visible = FALSE;
2721 was_visible = t->visible;
2722 if ( t == vtl->current_track ) {
2723 vtl->current_track = NULL;
2725 if ( t == vtl->route_finder_current_track )
2726 vtl->route_finder_current_track = NULL;
2728 /* could be current_tp, so we have to check */
2729 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2731 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2732 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2733 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2735 /* do this last because trk_name may be pointing to actual orig key */
2736 g_hash_table_remove ( vtl->tracks, trk_name );
2741 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2743 gboolean was_visible = FALSE;
2746 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2750 if ( wp == vtl->current_wp ) {
2751 vtl->current_wp = NULL;
2752 vtl->current_wp_name = NULL;
2753 vtl->moving_wp = FALSE;
2756 was_visible = wp->visible;
2757 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2758 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2759 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2761 highest_wp_number_remove_wp(vtl, wp_name);
2762 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2768 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2770 vik_treeview_item_delete (vt, it );
2773 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2776 vtl->current_track = NULL;
2777 vtl->route_finder_current_track = NULL;
2778 if (vtl->current_tp_track_name)
2779 trw_layer_cancel_current_tp(vtl, FALSE);
2780 if (vtl->last_tp_track_name)
2781 trw_layer_cancel_last_tp ( vtl );
2783 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2784 g_hash_table_remove_all(vtl->tracks_iters);
2785 g_hash_table_remove_all(vtl->tracks);
2787 /* TODO: only update if the layer is visible (ticked) */
2788 vik_layer_emit_update ( VIK_LAYER(vtl) );
2791 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2793 vtl->current_wp = NULL;
2794 vtl->current_wp_name = NULL;
2795 vtl->moving_wp = FALSE;
2797 highest_wp_number_reset(vtl);
2799 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2800 g_hash_table_remove_all(vtl->waypoints_iters);
2801 g_hash_table_remove_all(vtl->waypoints);
2803 /* TODO: only update if the layer is visible (ticked) */
2804 vik_layer_emit_update ( VIK_LAYER(vtl) );
2807 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
2809 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2810 // Get confirmation from the user
2811 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2812 _("Are you sure you want to delete all tracks in %s?"),
2813 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2814 vik_trw_layer_delete_all_tracks (vtl);
2817 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
2819 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
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 all waypoints in %s?"),
2823 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2824 vik_trw_layer_delete_all_waypoints (vtl);
2827 static void trw_layer_delete_item ( gpointer pass_along[6] )
2829 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2830 gboolean was_visible = FALSE;
2831 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2833 if ( GPOINTER_TO_INT ( pass_along[4]) )
2834 // Get confirmation from the user
2835 // Maybe this Waypoint Delete should be optional as is it could get annoying...
2836 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2837 _("Are you sure you want to delete the waypoint \"%s\""),
2840 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2844 if ( GPOINTER_TO_INT ( pass_along[4]) )
2845 // Get confirmation from the user
2846 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2847 _("Are you sure you want to delete the track \"%s\""),
2850 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2853 vik_layer_emit_update ( VIK_LAYER(vtl) );
2857 static void trw_layer_properties_item ( gpointer pass_along[6] )
2859 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2860 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2862 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2865 gboolean updated = FALSE;
2866 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
2868 if ( updated && VIK_LAYER(vtl)->visible )
2869 vik_layer_emit_update ( VIK_LAYER(vtl) );
2874 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2877 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2879 pass_along[1], /* vlp */
2880 pass_along[3], /* track name */
2881 pass_along[5] ); /* vvp */
2887 Parameter 1 -> VikLayersPanel
2888 Parameter 2 -> VikLayer
2889 Parameter 3 -> VikViewport
2891 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2894 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2895 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2898 /* since vlp not set, vl & vvp should be valid instead! */
2900 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2901 vik_layer_emit_update ( VIK_LAYER(vl) );
2906 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
2908 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2909 if ( trps && trps->data )
2910 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2913 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
2915 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2916 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2917 if ( trps && *trps )
2919 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2921 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2922 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2923 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2924 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2925 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
2929 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2931 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2932 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2934 vtl->current_track = track;
2935 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2937 if ( track->trackpoints )
2938 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2942 * extend a track using route finder
2944 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
2946 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2947 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2948 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2950 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2951 vtl->route_finder_coord = last_coord;
2952 vtl->route_finder_current_track = track;
2953 vtl->route_finder_started = TRUE;
2955 if ( track->trackpoints )
2956 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
2960 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2962 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2963 /* Also warn if overwrite old elevation data */
2964 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2966 vik_track_apply_dem_data ( track );
2969 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2971 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2974 trps = g_list_last(trps);
2975 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2978 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
2980 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2983 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2986 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
2988 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2991 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2994 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
2996 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2999 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3003 * Automatically change the viewport to center on the track and zoom to see the extent of the track
3005 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
3007 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3008 if ( trps && *trps )
3010 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3011 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3012 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3013 if ( pass_along[1] )
3014 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3016 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
3020 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3022 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3023 trw_layer_tpwin_init ( vtl );
3026 /*************************************
3027 * merge/split by time routines
3028 *************************************/
3030 /* called for each key in track hash table.
3031 * If the current track has the same time stamp type, add it to the result,
3032 * except the one pointed by "exclude".
3033 * set exclude to NULL if there is no exclude to check.
3034 * Note that the result is in reverse (for performance reasons).
3039 gboolean with_timestamps;
3041 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3043 twt_udata *user_data = udata;
3044 VikTrackpoint *p1, *p2;
3046 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3050 if (VIK_TRACK(value)->trackpoints) {
3051 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3052 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3054 if ( user_data->with_timestamps ) {
3055 if (!p1->has_timestamp || !p2->has_timestamp) {
3060 // Don't add tracks with timestamps when getting non timestamp tracks
3061 if (p1->has_timestamp || p2->has_timestamp) {
3067 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3070 /* called for each key in track hash table. if original track user_data[1] is close enough
3071 * to the passed one, add it to list in user_data[0]
3073 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3076 VikTrackpoint *p1, *p2;
3078 GList **nearby_tracks = ((gpointer *)user_data)[0];
3079 GList *orig_track = ((gpointer *)user_data)[1];
3080 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3083 * detect reasons for not merging, and return
3084 * if no reason is found not to merge, then do it.
3087 if (VIK_TRACK(value)->trackpoints == orig_track) {
3091 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3092 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3094 if (VIK_TRACK(value)->trackpoints) {
3095 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3096 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3098 if (!p1->has_timestamp || !p2->has_timestamp) {
3099 g_print("no timestamp\n");
3103 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3104 if (! (abs(t1 - p2->timestamp) < thr*60 ||
3106 abs(p1->timestamp - t2) < thr*60)
3113 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3116 /* comparison function used to sort tracks; a and b are hash table keys */
3117 /* Not actively used - can be restored if needed
3118 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3120 GHashTable *tracks = user_data;
3123 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3124 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3126 if (t1 < t2) return -1;
3127 if (t1 > t2) return 1;
3132 /* comparison function used to sort trackpoints */
3133 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3135 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3137 if (t1 < t2) return -1;
3138 if (t1 > t2) return 1;
3142 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3144 * comparison function which can be used to sort tracks or waypoints by name
3146 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3148 const gchar* namea = (const gchar*) a;
3149 const gchar* nameb = (const gchar*) b;
3150 if ( namea == NULL || nameb == NULL)
3153 // Same sort method as used in the vik_treeview_*_alphabetize functions
3154 return strcmp ( namea, nameb );
3159 * Attempt to merge selected track with other tracks specified by the user
3160 * Tracks to merge with must be of the same 'type' as the selected track -
3161 * either all with timestamps, or all without timestamps
3163 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3165 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3166 gchar *orig_track_name = pass_along[3];
3167 GList *other_tracks = NULL;
3168 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3170 if ( !track->trackpoints )
3174 udata.result = &other_tracks;
3175 udata.exclude = track->trackpoints;
3176 // Allow merging with 'similar' time type time tracks
3177 // i.e. either those times, or those without
3178 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3180 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3181 other_tracks = g_list_reverse(other_tracks);
3183 if ( !other_tracks ) {
3184 if ( udata.with_timestamps )
3185 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3187 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3191 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3192 // Sort alphabetically for user presentation
3193 other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3196 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3198 _("Merge with..."), _("Select track to merge with"));
3199 g_list_free(other_tracks);
3204 for (l = merge_list; l != NULL; l = g_list_next(l)) {
3205 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3207 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3208 merge_track->trackpoints = NULL;
3209 vik_trw_layer_delete_track(vtl, l->data);
3210 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3213 /* TODO: free data before free merge_list */
3214 for (l = merge_list; l != NULL; l = g_list_next(l))
3216 g_list_free(merge_list);
3217 vik_layer_emit_update( VIK_LAYER(vtl) );
3221 /* merge by time routine */
3222 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3224 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3225 gchar *orig_track_name = strdup(pass_along[3]);
3228 GList *nearby_tracks;
3231 static guint thr = 1;
3232 guint track_count = 0;
3234 GList *tracks_with_timestamp = NULL;
3235 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3236 if (track->trackpoints &&
3237 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3238 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3239 free(orig_track_name);
3244 udata.result = &tracks_with_timestamp;
3245 udata.exclude = track->trackpoints;
3246 udata.with_timestamps = TRUE;
3247 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3248 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3250 if (!tracks_with_timestamp) {
3251 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3252 free(orig_track_name);
3255 g_list_free(tracks_with_timestamp);
3257 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3258 _("Merge Threshold..."),
3259 _("Merge when time between tracks less than:"),
3261 free(orig_track_name);
3265 /* merge tracks until we can't */
3266 nearby_tracks = NULL;
3270 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3271 trps = track->trackpoints;
3276 if (nearby_tracks) {
3277 g_list_free(nearby_tracks);
3278 nearby_tracks = NULL;
3281 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3282 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3284 /* g_print("Original track times: %d and %d\n", t1, t2); */
3285 params[0] = &nearby_tracks;
3287 params[2] = GUINT_TO_POINTER (thr);
3289 /* get a list of adjacent-in-time tracks */
3290 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3292 /* add original track */
3293 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3297 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3298 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3299 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3300 GList *l = nearby_tracks;
3301 VikTrack *tr = vik_track_new();
3302 tr->visible = track->visible;
3307 t1 = get_first_trackpoint(l)->timestamp;
3308 t2 = get_last_trackpoint(l)->timestamp;
3309 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3313 /* remove trackpoints from merged track, delete track */
3314 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3315 get_track(l)->trackpoints = NULL;
3316 vik_trw_layer_delete_track(vtl, l->data);
3321 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3322 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3324 #undef get_first_trackpoint
3325 #undef get_last_trackpoint
3328 } while (track_count > 1);
3329 g_list_free(nearby_tracks);
3330 free(orig_track_name);
3331 vik_layer_emit_update( VIK_LAYER(vtl) );
3334 /* split by time routine */
3335 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3337 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3338 GList *trps = track->trackpoints;
3340 GList *newlists = NULL;
3341 GList *newtps = NULL;
3343 static guint thr = 1;
3350 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3351 _("Split Threshold..."),
3352 _("Split when time between trackpoints exceeds:"),
3357 /* iterate through trackpoints, and copy them into new lists without touching original list */
3358 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3362 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3364 g_print("panic: ts < prev_ts: this should never happen!\n");
3367 if (ts - prev_ts > thr*60) {
3368 /* flush accumulated trackpoints into new list */
3369 newlists = g_list_append(newlists, g_list_reverse(newtps));
3373 /* accumulate trackpoint copies in newtps, in reverse order */
3374 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3376 iter = g_list_next(iter);
3379 newlists = g_list_append(newlists, g_list_reverse(newtps));
3382 /* put lists of trackpoints into tracks */
3385 // Only bother updating if the split results in new tracks
3386 if (g_list_length (newlists) > 1) {
3391 tr = vik_track_new();
3392 tr->visible = track->visible;
3393 tr->trackpoints = (GList *)(iter->data);
3395 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3396 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3397 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3398 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3400 iter = g_list_next(iter);
3402 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3403 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3405 g_list_free(newlists);
3409 * Split a track by the number of points as specified by the user
3411 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3413 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3414 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3416 // Check valid track
3417 GList *trps = track->trackpoints;
3421 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3422 _("Split Every Nth Point"),
3423 _("Split on every Nth point:"),
3424 250, // Default value as per typical limited track capacity of various GPS devices
3428 // Was a valid number returned?
3434 GList *newlists = NULL;
3435 GList *newtps = NULL;
3440 /* accumulate trackpoint copies in newtps, in reverse order */
3441 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3443 if (count >= points) {
3444 /* flush accumulated trackpoints into new list */
3445 newlists = g_list_append(newlists, g_list_reverse(newtps));
3449 iter = g_list_next(iter);
3452 // If there is a remaining chunk put that into the new split list
3453 // This may well be the whole track if no split points were encountered
3455 newlists = g_list_append(newlists, g_list_reverse(newtps));
3458 /* put lists of trackpoints into tracks */
3461 // Only bother updating if the split results in new tracks
3462 if (g_list_length (newlists) > 1) {
3467 tr = vik_track_new();
3468 tr->visible = track->visible;
3469 tr->trackpoints = (GList *)(iter->data);
3471 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3472 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3474 iter = g_list_next(iter);
3476 // Remove original track and then update the display
3477 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3478 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3480 g_list_free(newlists);
3483 /* end of split/merge routines */
3486 * Similar to trw_layer_enum_item, but this uses a sorted method
3488 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3490 GList **list = (GList**)udata;
3491 //*list = g_list_prepend(*all, key); //unsorted method
3492 // Sort named list alphabetically
3493 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3499 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3501 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3503 // Sort list alphabetically for better presentation
3504 g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3507 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3511 // Get list of items to delete from the user
3512 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3515 _("Delete Selection"),
3516 _("Select tracks to delete"));
3519 // Delete requested tracks
3520 // since specificly requested, IMHO no need for extra confirmation
3521 if ( delete_list ) {
3523 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3524 vik_trw_layer_delete_track(vtl, l->data);
3526 g_list_free(delete_list);
3527 vik_layer_emit_update( VIK_LAYER(vtl) );
3534 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3536 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3539 // Sort list alphabetically for better presentation
3540 g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3542 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3546 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3548 // Get list of items to delete from the user
3549 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3552 _("Delete Selection"),
3553 _("Select waypoints to delete"));
3556 // Delete requested waypoints
3557 // since specificly requested, IMHO no need for extra confirmation
3558 if ( delete_list ) {
3560 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3561 vik_trw_layer_delete_waypoint(vtl, l->data);
3563 g_list_free(delete_list);
3564 vik_layer_emit_update( VIK_LAYER(vtl) );
3569 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3571 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3573 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3576 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3578 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3579 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3583 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3585 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3590 if (strcmp(newname, sublayer) == 0 )
3593 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3594 if (g_hash_table_lookup( l->waypoints, newname))
3596 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3601 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3602 g_hash_table_steal ( l->waypoints_iters, sublayer );
3604 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3605 highest_wp_number_remove_wp(l, sublayer);
3606 g_hash_table_remove ( l->waypoints, sublayer );
3608 rv = g_strdup(newname);
3610 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3612 highest_wp_number_add_wp(l, rv);
3613 g_hash_table_insert ( l->waypoints, rv, wp );
3614 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3616 /* it hasn't been updated yet so we pass new name */
3617 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3618 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3621 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3624 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3631 if (strcmp(newname, sublayer) == 0)
3634 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3635 if (g_hash_table_lookup( l->tracks, newname))
3637 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3642 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3643 g_hash_table_steal ( l->tracks, sublayer );
3645 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3646 g_hash_table_steal ( l->tracks_iters, sublayer );
3648 rv = g_strdup(newname);
3650 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3652 g_hash_table_insert ( l->tracks, rv, tr );
3653 g_hash_table_insert ( l->tracks_iters, rv, iter );
3655 /* don't forget about current_tp_track_name, update that too */
3656 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3658 l->current_tp_track_name = rv;
3660 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3662 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3663 l->last_tp_track_name = rv;
3665 g_free ( orig_key );
3667 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3668 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3671 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3677 static gboolean is_valid_geocache_name ( gchar *str )
3679 gint len = strlen ( str );
3680 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]));
3683 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3685 gchar *track_name = (gchar *) pass_along[3];
3686 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3687 a_acquire_set_filter_track ( tr, track_name );
3690 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3692 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3693 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3696 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3698 gchar *track_name = (gchar *) pass_along[3];
3699 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3701 gchar *escaped = uri_escape ( tr->comment );
3702 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3703 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3709 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3710 /* viewpoint is now available instead */
3711 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3713 static gpointer pass_along[6];
3715 gboolean rv = FALSE;
3718 pass_along[1] = vlp;
3719 pass_along[2] = GINT_TO_POINTER (subtype);
3720 pass_along[3] = sublayer;
3721 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
3722 pass_along[5] = vvp;
3724 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3728 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3729 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3730 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3731 gtk_widget_show ( item );
3733 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3734 VikTrwLayer *vtl = l;
3735 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3736 if (tr && tr->property_dialog)
3737 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3740 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3741 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3742 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3743 gtk_widget_show ( item );
3745 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3746 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3747 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3748 gtk_widget_show ( item );
3750 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3752 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3753 gtk_widget_show ( item );
3755 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3757 gboolean separator_created = FALSE;
3759 /* could be a right-click using the tool */
3760 if ( vlp != NULL ) {
3761 item = gtk_menu_item_new ();
3762 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3763 gtk_widget_show ( item );
3765 separator_created = TRUE;
3767 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
3768 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3770 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3771 gtk_widget_show ( item );
3774 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3776 if ( !separator_created ) {
3777 item = gtk_menu_item_new ();
3778 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3779 gtk_widget_show ( item );
3780 separator_created = TRUE;
3783 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3784 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3785 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3786 gtk_widget_show ( item );
3789 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
3791 if ( wp && wp->image )
3793 if ( !separator_created ) {
3794 item = gtk_menu_item_new ();
3795 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3796 gtk_widget_show ( item );
3797 separator_created = TRUE;
3800 // Set up image paramater
3801 pass_along[5] = wp->image;
3803 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
3804 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
3805 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
3806 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3807 gtk_widget_show ( item );
3813 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3816 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3817 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3819 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3820 gtk_widget_show ( item );
3823 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
3825 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
3826 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3828 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3829 gtk_widget_show ( item );
3831 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3832 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3833 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3834 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3835 gtk_widget_show ( item );
3837 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
3838 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3839 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3840 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3841 gtk_widget_show ( item );
3843 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
3844 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3846 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3847 gtk_widget_show ( item );
3850 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
3854 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
3855 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3856 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3857 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3858 gtk_widget_show ( item );
3860 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
3861 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3863 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3864 gtk_widget_show ( item );
3866 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
3867 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3868 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3869 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3870 gtk_widget_show ( item );
3873 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3875 GtkWidget *goto_submenu;
3876 item = gtk_menu_item_new ();
3877 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3878 gtk_widget_show ( item );
3880 goto_submenu = gtk_menu_new ();
3881 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
3882 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3883 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3884 gtk_widget_show ( item );
3885 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3887 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
3888 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
3889 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3890 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3891 gtk_widget_show ( item );
3893 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3896 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3897 gtk_widget_show ( item );
3899 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
3900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
3901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3902 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3903 gtk_widget_show ( item );
3905 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3906 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
3907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3908 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3909 gtk_widget_show ( item );
3911 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3912 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
3913 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3914 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3915 gtk_widget_show ( item );
3917 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3918 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
3919 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3920 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3921 gtk_widget_show ( item );
3923 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
3924 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3925 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3926 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3927 gtk_widget_show ( item );
3929 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
3930 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3931 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3932 gtk_widget_show ( item );
3934 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3936 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3937 gtk_widget_show ( item );
3939 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
3940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3941 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3942 gtk_widget_show ( item );
3944 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
3945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
3946 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3947 gtk_widget_show ( item );
3949 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
3951 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3952 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
3953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3954 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3955 gtk_widget_show ( item );
3958 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3959 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
3960 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3961 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3962 gtk_widget_show ( item );
3964 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
3965 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3966 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3967 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3968 gtk_widget_show ( item );
3970 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3971 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3973 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3974 gtk_widget_show ( item );
3976 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
3977 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
3978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
3979 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3980 gtk_widget_show ( item );
3982 #ifdef VIK_CONFIG_OPENSTREETMAP
3983 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3985 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3986 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3987 gtk_widget_show ( item );
3990 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3992 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3993 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
3994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3995 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3996 gtk_widget_show ( item );
3999 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4000 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4001 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4002 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4003 gtk_widget_show ( item );
4005 /* ATM This function is only available via the layers panel, due to needing a vlp */
4007 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4008 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4009 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4011 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4012 gtk_widget_show ( item );
4016 // Only show on viewport popmenu when a trackpoint is selected
4017 if ( ! vlp && l->current_tpl ) {
4019 item = gtk_menu_item_new ();
4020 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4021 gtk_widget_show ( item );
4023 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4024 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4026 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4027 gtk_widget_show ( item );
4035 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4038 if (!vtl->current_tpl)
4040 if (!vtl->current_tpl->next)
4043 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4044 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4046 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4049 VikTrackpoint *tp_new = vik_trackpoint_new();
4050 struct LatLon ll_current, ll_next;
4051 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4052 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4054 /* main positional interpolation */
4055 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4056 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4058 /* Now other properties that can be interpolated */
4059 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4061 if (tp_current->has_timestamp && tp_next->has_timestamp) {
4062 /* Note here the division is applied to each part, then added
4063 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4064 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4065 tp_new->has_timestamp = TRUE;
4068 if (tp_current->speed != NAN && tp_next->speed != NAN)
4069 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4071 /* TODO - improve interpolation of course, as it may not be correct.
4072 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4073 [similar applies if value is in radians] */
4074 if (tp_current->course != NAN && tp_next->course != NAN)
4075 tp_new->speed = (tp_current->course + tp_next->course)/2;
4077 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4079 /* Insert new point into the trackpoints list after the current TP */
4080 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4081 gint index = g_list_index ( tr->trackpoints, tp_current );
4083 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4088 /* to be called when last_tpl no long exists. */
4089 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4091 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4092 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4093 vtl->last_tpl = NULL;
4094 vtl->last_tp_track_name = NULL;
4097 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4103 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4107 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4109 if ( vtl->current_tpl )
4111 vtl->current_tpl = NULL;
4112 vtl->current_tp_track_name = NULL;
4113 vik_layer_emit_update(VIK_LAYER(vtl));
4117 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4119 g_assert ( vtl->tpwin != NULL );
4120 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4121 trw_layer_cancel_current_tp ( vtl, TRUE );
4123 if ( vtl->current_tpl == NULL )
4126 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4128 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4129 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4131 VikTrack *tr = vik_track_new ();
4132 GList *newglist = g_list_alloc ();
4133 newglist->prev = NULL;
4134 newglist->next = vtl->current_tpl->next;
4135 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4136 tr->trackpoints = newglist;
4138 vtl->current_tpl->next->prev = newglist; /* end old track here */
4139 vtl->current_tpl->next = NULL;
4141 vtl->current_tpl = newglist; /* change tp to first of new track. */
4142 vtl->current_tp_track_name = name;
4144 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4148 vik_trw_layer_add_track ( vtl, name, tr );
4149 vik_layer_emit_update(VIK_LAYER(vtl));
4152 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4154 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4156 g_assert(tr != NULL);
4158 /* can't join with a non-existent trackpoint */
4159 vtl->last_tpl = NULL;
4160 vtl->last_tp_track_name = NULL;
4162 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4164 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4165 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4167 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4169 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4170 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4172 trw_layer_cancel_last_tp ( vtl );
4174 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4175 g_list_free_1 ( vtl->current_tpl );
4176 vtl->current_tpl = new_tpl;
4177 vik_layer_emit_update(VIK_LAYER(vtl));
4181 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4182 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4183 g_list_free_1 ( vtl->current_tpl );
4184 trw_layer_cancel_current_tp ( vtl, FALSE );
4187 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4189 vtl->last_tpl = vtl->current_tpl;
4190 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4191 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
4193 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4195 vtl->last_tpl = vtl->current_tpl;
4196 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4197 vik_layer_emit_update(VIK_LAYER(vtl));
4199 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4201 // Check tracks exist and are different before joining
4202 if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4205 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4206 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4208 VikTrack *tr_first = tr1, *tr_last = tr2;
4212 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4213 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4214 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4215 vik_track_reverse ( tr1 );
4216 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4221 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4223 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. */
4224 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4225 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4226 tr2->trackpoints = NULL;
4228 tmp = vtl->current_tp_track_name;
4230 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4231 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4233 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4234 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4235 vik_trw_layer_delete_track ( vtl, tmp );
4237 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4238 vik_layer_emit_update(VIK_LAYER(vtl));
4240 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4242 trw_layer_insert_tp_after_current_tp ( vtl );
4243 vik_layer_emit_update(VIK_LAYER(vtl));
4245 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4246 vik_layer_emit_update (VIK_LAYER(vtl));
4249 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4253 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4254 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4255 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4256 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4257 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4259 if ( vtl->current_tpl )
4260 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4261 /* set layer name and TP data */
4264 /***************************************************************************
4266 ***************************************************************************/
4268 /*** Utility data structures and functions ****/
4272 gint closest_x, closest_y;
4273 gchar *closest_wp_name;
4274 VikWaypoint *closest_wp;
4280 gint closest_x, closest_y;
4281 gchar *closest_track_name;
4282 VikTrackpoint *closest_tp;
4287 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4293 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4295 // If waypoint has an image then use the image size to select
4297 gint slackx, slacky;
4298 slackx = wp->image_width / 2;
4299 slacky = wp->image_height / 2;
4301 if ( x <= params->x + slackx && x >= params->x - slackx
4302 && y <= params->y + slacky && y >= params->y - slacky ) {
4303 params->closest_wp_name = name;
4304 params->closest_wp = wp;
4305 params->closest_x = x;
4306 params->closest_y = y;
4309 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4310 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
4311 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4313 params->closest_wp_name = name;
4314 params->closest_wp = wp;
4315 params->closest_x = x;
4316 params->closest_y = y;
4320 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4322 GList *tpl = t->trackpoints;
4331 tp = VIK_TRACKPOINT(tpl->data);
4333 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4335 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4336 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
4337 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4339 params->closest_track_name = name;
4340 params->closest_tp = tp;
4341 params->closest_tpl = tpl;
4342 params->closest_x = x;
4343 params->closest_y = y;
4349 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4351 TPSearchParams params;
4355 params.closest_track_name = NULL;
4356 params.closest_tp = NULL;
4357 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4358 return params.closest_tp;
4361 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4363 WPSearchParams params;
4367 params.closest_wp = NULL;
4368 params.closest_wp_name = NULL;
4369 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4370 return params.closest_wp;
4373 // Some forward declarations
4374 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4375 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4376 static void marker_end_move ( tool_ed_t *t );
4379 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4383 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4385 // Here always allow snapping back to the original location
4386 // this is useful when one decides not to move the thing afterall
4387 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4390 if ( event->state & GDK_CONTROL_MASK )
4392 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4394 new_coord = tp->coord;
4398 if ( event->state & GDK_SHIFT_MASK )
4400 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4402 new_coord = wp->coord;
4406 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4408 marker_moveto ( t, x, y );
4415 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4417 if ( t->holding && event->button == 1 )
4420 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4423 if ( event->state & GDK_CONTROL_MASK )
4425 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4427 new_coord = tp->coord;
4431 if ( event->state & GDK_SHIFT_MASK )
4433 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4435 new_coord = wp->coord;
4438 marker_end_move ( t );
4440 // Determine if working on a waypoint or a trackpoint
4441 if ( t->is_waypoint )
4442 vtl->current_wp->coord = new_coord;
4444 if ( vtl->current_tpl ) {
4445 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4448 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4450 // Don't really know what this is for but seems like it might be handy...
4451 /* can't join with itself! */
4452 trw_layer_cancel_last_tp ( vtl );
4457 vtl->current_wp = NULL;
4458 vtl->current_wp_name = NULL;
4459 trw_layer_cancel_current_tp ( vtl, FALSE );
4461 vik_layer_emit_update ( VIK_LAYER(vtl) );
4468 Returns true if a waypoint or track is found near the requested event position for this particular layer
4469 The item found is automatically selected
4470 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4472 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4474 if ( event->button != 1 )
4477 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4480 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4483 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4485 if (vtl->waypoints_visible) {
4486 WPSearchParams wp_params;
4487 wp_params.vvp = vvp;
4488 wp_params.x = event->x;
4489 wp_params.y = event->y;
4490 wp_params.closest_wp_name = NULL;
4491 wp_params.closest_wp = NULL;
4493 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4495 if ( wp_params.closest_wp ) {
4498 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4500 // Too easy to move it so must be holding shift to start immediately moving it
4501 // or otherwise be previously selected
4502 if ( event->state & GDK_SHIFT_MASK ||
4503 vtl->current_wp == wp_params.closest_wp ) {
4504 // Put into 'move buffer'
4505 // NB vvp & vw already set in tet
4506 tet->vtl = (gpointer)vtl;
4507 tet->is_waypoint = TRUE;
4509 marker_begin_move (tet, event->x, event->y);
4512 vtl->current_wp = wp_params.closest_wp;
4513 vtl->current_wp_name = wp_params.closest_wp_name;
4515 vik_layer_emit_update ( VIK_LAYER(vtl) );
4521 if (vtl->tracks_visible) {
4522 TPSearchParams tp_params;
4523 tp_params.vvp = vvp;
4524 tp_params.x = event->x;
4525 tp_params.y = event->y;
4526 tp_params.closest_track_name = NULL;
4527 tp_params.closest_tp = NULL;
4529 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4531 if ( tp_params.closest_tp ) {
4533 // Always select + highlight the track
4534 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4536 tet->is_waypoint = FALSE;
4538 // Select the Trackpoint
4539 // Can move it immediately when control held or it's the previously selected tp
4540 if ( event->state & GDK_CONTROL_MASK ||
4541 vtl->current_tpl == tp_params.closest_tpl ) {
4542 // Put into 'move buffer'
4543 // NB vvp & vw already set in tet
4544 tet->vtl = (gpointer)vtl;
4545 marker_begin_move (tet, event->x, event->y);
4548 vtl->current_tpl = tp_params.closest_tpl;
4549 vtl->current_tp_track_name = tp_params.closest_track_name;
4552 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4554 vik_layer_emit_update ( VIK_LAYER(vtl) );
4559 /* these aren't the droids you're looking for */
4560 vtl->current_wp = NULL;
4561 vtl->current_wp_name = NULL;
4562 trw_layer_cancel_current_tp ( vtl, FALSE );
4567 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4569 if ( event->button != 3 )
4572 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4575 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4578 /* Post menu for the currently selected item */
4580 /* See if a track is selected */
4581 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4582 if ( track && track->visible ) {
4584 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4586 if ( vtl->track_right_click_menu )
4587 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4589 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4591 trw_layer_sublayer_add_menu_items ( vtl,
4592 vtl->track_right_click_menu,
4594 VIK_TRW_LAYER_SUBLAYER_TRACK,
4595 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4596 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4599 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4605 /* See if a waypoint is selected */
4606 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4607 if ( waypoint && waypoint->visible ) {
4608 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4610 if ( vtl->wp_right_click_menu )
4611 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4613 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4614 trw_layer_sublayer_add_menu_items ( vtl,
4615 vtl->wp_right_click_menu,
4617 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4618 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4619 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4621 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4630 /* background drawing hook, to be passed the viewport */
4631 static gboolean tool_sync_done = TRUE;
4633 static gboolean tool_sync(gpointer data)
4635 VikViewport *vvp = data;
4636 gdk_threads_enter();
4637 vik_viewport_sync(vvp);
4638 tool_sync_done = TRUE;
4639 gdk_threads_leave();
4643 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4646 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4647 gdk_gc_set_function ( t->gc, GDK_INVERT );
4648 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4649 vik_viewport_sync(t->vvp);
4654 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4656 VikViewport *vvp = t->vvp;
4657 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4658 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4662 if (tool_sync_done) {
4663 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4664 tool_sync_done = FALSE;
4668 static void marker_end_move ( tool_ed_t *t )
4670 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4671 g_object_unref ( t->gc );
4675 /*** Edit waypoint ****/
4677 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4679 tool_ed_t *t = g_new(tool_ed_t, 1);
4685 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4687 WPSearchParams params;
4688 tool_ed_t *t = data;
4689 VikViewport *vvp = t->vvp;
4691 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4698 if ( !vtl->vl.visible || !vtl->waypoints_visible )
4701 if ( vtl->current_wp && vtl->current_wp->visible )
4703 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
4705 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
4707 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
4708 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
4710 if ( event->button == 3 )
4711 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4713 marker_begin_move(t, event->x, event->y);
4720 params.x = event->x;
4721 params.y = event->y;
4722 params.closest_wp_name = NULL;
4723 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4724 params.closest_wp = NULL;
4725 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
4726 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
4728 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
4729 marker_begin_move(t, event->x, event->y);
4730 g_critical("shouldn't be here");
4733 else if ( params.closest_wp )
4735 if ( event->button == 3 )
4736 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4738 vtl->waypoint_rightclick = FALSE;
4740 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
4742 vtl->current_wp = params.closest_wp;
4743 vtl->current_wp_name = params.closest_wp_name;
4745 /* could make it so don't update if old WP is off screen and new is null but oh well */
4746 vik_layer_emit_update ( VIK_LAYER(vtl) );
4750 vtl->current_wp = NULL;
4751 vtl->current_wp_name = NULL;
4752 vtl->waypoint_rightclick = FALSE;
4753 vik_layer_emit_update ( VIK_LAYER(vtl) );
4757 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4759 tool_ed_t *t = data;
4760 VikViewport *vvp = t->vvp;
4762 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4767 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4770 if ( event->state & GDK_CONTROL_MASK )
4772 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4774 new_coord = tp->coord;
4778 if ( event->state & GDK_SHIFT_MASK )
4780 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4781 if ( wp && wp != vtl->current_wp )
4782 new_coord = wp->coord;
4787 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4789 marker_moveto ( t, x, y );
4796 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4798 tool_ed_t *t = data;
4799 VikViewport *vvp = t->vvp;
4801 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4804 if ( t->holding && event->button == 1 )
4807 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4810 if ( event->state & GDK_CONTROL_MASK )
4812 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4814 new_coord = tp->coord;
4818 if ( event->state & GDK_SHIFT_MASK )
4820 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4821 if ( wp && wp != vtl->current_wp )
4822 new_coord = wp->coord;
4825 marker_end_move ( t );
4827 vtl->current_wp->coord = new_coord;
4828 vik_layer_emit_update ( VIK_LAYER(vtl) );
4831 /* PUT IN RIGHT PLACE!!! */
4832 if ( event->button == 3 && vtl->waypoint_rightclick )
4834 if ( vtl->wp_right_click_menu )
4835 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
4836 if ( vtl->current_wp ) {
4837 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4838 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 );
4839 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4841 vtl->waypoint_rightclick = FALSE;
4846 /**** Begin track ***/
4847 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4852 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4854 vtl->current_track = NULL;
4855 return tool_new_track_click ( vtl, event, vvp );
4858 /*** New track ****/
4860 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4868 gint x1,y1,x2,y2,x3,y3;
4870 } new_track_move_passalong_t;
4872 /* sync and undraw, but only when we have time */
4873 static gboolean ct_sync ( gpointer passalong )
4875 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4877 vik_viewport_sync ( p->vvp );
4878 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4879 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4880 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);
4881 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4883 g_free ( (gpointer) p->str ) ;
4884 p->vtl->ct_sync_done = TRUE;
4889 static const gchar* distance_string (gdouble distance)
4893 /* draw label with distance */
4894 vik_units_distance_t dist_units = a_vik_get_units_distance ();
4895 switch (dist_units) {
4896 case VIK_UNITS_DISTANCE_MILES:
4897 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
4898 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
4899 } else if (distance < 1609.4) {
4900 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
4902 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
4906 // VIK_UNITS_DISTANCE_KILOMETRES
4907 if (distance >= 1000 && distance < 100000) {
4908 g_sprintf(str, "%3.2f km", distance/1000.0);
4909 } else if (distance < 1000) {
4910 g_sprintf(str, "%d m", (int)distance);
4912 g_sprintf(str, "%d km", (int)distance/1000);
4916 return g_strdup (str);
4920 * Actually set the message in statusbar
4922 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
4924 // Only show elevation data when track has some elevation properties
4925 gchar str_gain_loss[64];
4926 str_gain_loss[0] = '\0';
4928 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
4929 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
4930 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
4932 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
4935 // Write with full gain/loss information
4936 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
4937 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
4942 * Figure out what information should be set in the statusbar and then write it
4944 static void update_statusbar ( VikTrwLayer *vtl )
4946 // Get elevation data
4947 gdouble elev_gain, elev_loss;
4948 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
4950 /* Find out actual distance of current track */
4951 gdouble distance = vik_track_get_length (vtl->current_track);
4952 const gchar *str = distance_string (distance);
4954 statusbar_write (str, elev_gain, elev_loss, vtl);
4956 g_free ((gpointer)str);
4960 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
4962 /* if we haven't sync'ed yet, we don't have time to do more. */
4963 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
4964 GList *iter = vtl->current_track->trackpoints;
4965 new_track_move_passalong_t *passalong;
4968 while ( iter->next )
4970 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
4971 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
4972 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
4974 /* Find out actual distance of current track */
4975 gdouble distance = vik_track_get_length (vtl->current_track);
4977 // Now add distance to where the pointer is //
4980 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
4981 vik_coord_to_latlon ( &coord, &ll );
4982 distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
4984 // Get elevation data
4985 gdouble elev_gain, elev_loss;
4986 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
4988 // Adjust elevation data (if available) for the current pointer position
4990 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
4991 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
4992 if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
4993 // Adjust elevation of last track point
4994 if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
4996 elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
4999 elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5003 const gchar *str = distance_string (distance);
5005 /* offset from cursor a bit */
5008 /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5009 vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5011 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5013 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5014 passalong->vtl = vtl;
5015 passalong->vvp = vvp;
5018 passalong->x2 = event->x;
5019 passalong->y2 = event->y;
5022 passalong->str = str;
5024 // Update statusbar with full gain/loss information
5025 statusbar_write (str, elev_gain, elev_loss, vtl);
5027 /* this will sync and undraw when we have time to */
5028 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5029 vtl->ct_sync_done = FALSE;
5030 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5032 return VIK_LAYER_TOOL_ACK;
5035 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5037 if ( vtl->current_track && event->keyval == GDK_Escape ) {
5038 vtl->current_track = NULL;
5039 vik_layer_emit_update ( VIK_LAYER(vtl) );
5041 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
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 );
5050 update_statusbar ( vtl );
5052 vik_layer_emit_update ( VIK_LAYER(vtl) );
5058 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5062 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5065 if ( event->button == 3 && vtl->current_track )
5068 if ( vtl->current_track->trackpoints )
5070 GList *last = g_list_last(vtl->current_track->trackpoints);
5071 g_free ( last->data );
5072 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5074 update_statusbar ( vtl );
5076 vik_layer_emit_update ( VIK_LAYER(vtl) );
5080 if ( event->type == GDK_2BUTTON_PRESS )
5082 /* subtract last (duplicate from double click) tp then end */
5083 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5085 GList *last = g_list_last(vtl->current_track->trackpoints);
5086 g_free ( last->data );
5087 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5088 /* undo last, then end */
5089 vtl->current_track = NULL;
5091 vik_layer_emit_update ( VIK_LAYER(vtl) );
5095 if ( ! vtl->current_track )
5097 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5098 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5100 vtl->current_track = vik_track_new();
5101 vtl->current_track->visible = TRUE;
5102 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5104 /* incase it was created by begin track */
5105 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5110 tp = vik_trackpoint_new();
5111 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5113 /* snap to other TP */
5114 if ( event->state & GDK_CONTROL_MASK )
5116 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5118 tp->coord = other_tp->coord;
5121 tp->newsegment = FALSE;
5122 tp->has_timestamp = FALSE;
5124 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5125 /* Auto attempt to get elevation from DEM data (if it's available) */
5126 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5128 vtl->ct_x1 = vtl->ct_x2;
5129 vtl->ct_y1 = vtl->ct_y2;
5130 vtl->ct_x2 = event->x;
5131 vtl->ct_y2 = event->y;
5133 vik_layer_emit_update ( VIK_LAYER(vtl) );
5137 /*** New waypoint ****/
5139 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5144 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5147 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5149 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5150 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5151 vik_layer_emit_update ( VIK_LAYER(vtl) );
5156 /*** Edit trackpoint ****/
5158 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5160 tool_ed_t *t = g_new(tool_ed_t, 1);
5166 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5168 tool_ed_t *t = data;
5169 VikViewport *vvp = t->vvp;
5170 TPSearchParams params;
5171 /* OUTDATED DOCUMENTATION:
5172 find 5 pixel range on each side. then put these UTM, and a pointer
5173 to the winning track name (and maybe the winning track itself), and a
5174 pointer to the winning trackpoint, inside an array or struct. pass
5175 this along, do a foreach on the tracks which will do a foreach on the
5178 params.x = event->x;
5179 params.y = event->y;
5180 params.closest_track_name = NULL;
5181 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5182 params.closest_tp = NULL;
5184 if ( event->button != 1 )
5187 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5190 if ( !vtl->vl.visible || !vtl->tracks_visible )
5193 if ( vtl->current_tpl )
5195 /* 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.) */
5196 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5197 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5199 g_assert ( current_tr );
5201 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5203 if ( current_tr->visible &&
5204 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5205 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5206 marker_begin_move ( t, event->x, event->y );
5210 vtl->last_tpl = vtl->current_tpl;
5211 vtl->last_tp_track_name = vtl->current_tp_track_name;
5214 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
5216 if ( params.closest_tp )
5218 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5219 vtl->current_tpl = params.closest_tpl;
5220 vtl->current_tp_track_name = params.closest_track_name;
5221 trw_layer_tpwin_init ( vtl );
5222 vik_layer_emit_update ( VIK_LAYER(vtl) );
5226 /* these aren't the droids you're looking for */
5230 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5232 tool_ed_t *t = data;
5233 VikViewport *vvp = t->vvp;
5235 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5241 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5244 if ( event->state & GDK_CONTROL_MASK )
5246 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5247 if ( tp && tp != vtl->current_tpl->data )
5248 new_coord = tp->coord;
5250 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5253 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5254 marker_moveto ( t, x, y );
5262 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5264 tool_ed_t *t = data;
5265 VikViewport *vvp = t->vvp;
5267 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5269 if ( event->button != 1)
5274 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5277 if ( event->state & GDK_CONTROL_MASK )
5279 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5280 if ( tp && tp != vtl->current_tpl->data )
5281 new_coord = tp->coord;
5284 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5286 marker_end_move ( t );
5288 /* diff dist is diff from orig */
5290 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5291 /* can't join with itself! */
5292 trw_layer_cancel_last_tp ( vtl );
5294 vik_layer_emit_update ( VIK_LAYER(vtl) );
5301 /*** Route Finder ***/
5302 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5307 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5310 if ( !vtl ) return FALSE;
5311 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5312 if ( event->button == 3 && vtl->route_finder_current_track ) {
5314 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5316 vtl->route_finder_coord = *new_end;
5318 vik_layer_emit_update ( VIK_LAYER(vtl) );
5319 /* remove last ' to:...' */
5320 if ( vtl->route_finder_current_track->comment ) {
5321 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5322 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5323 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5324 last_to - vtl->route_finder_current_track->comment - 1);
5325 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5330 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5331 struct LatLon start, end;
5332 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5333 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5336 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5337 vik_coord_to_latlon ( &(tmp), &end );
5338 vtl->route_finder_coord = tmp; /* for continuations */
5340 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5341 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5342 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
5344 vtl->route_finder_check_added_track = TRUE;
5345 vtl->route_finder_started = FALSE;
5348 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5349 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5350 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5351 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5352 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5353 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5356 /* see if anything was done -- a track was added or appended to */
5357 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5360 tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5363 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5365 vtl->route_finder_current_track = tr;
5367 g_free ( vtl->route_finder_added_track_name );
5368 vtl->route_finder_added_track_name = NULL;
5369 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5370 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5371 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5372 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5374 vtl->route_finder_check_added_track = FALSE;
5375 vtl->route_finder_append = FALSE;
5377 vik_layer_emit_update ( VIK_LAYER(vtl) );
5379 vtl->route_finder_started = TRUE;
5380 vtl->route_finder_coord = tmp;
5381 vtl->route_finder_current_track = NULL;
5386 /*** Show picture ****/
5388 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5393 /* Params are: vvp, event, last match found or NULL */
5394 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
5396 if ( wp->image && wp->visible )
5398 gint x, y, slackx, slacky;
5399 GdkEventButton *event = (GdkEventButton *) params[1];
5401 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5402 slackx = wp->image_width / 2;
5403 slacky = wp->image_height / 2;
5404 if ( x <= event->x + slackx && x >= event->x - slackx
5405 && y <= event->y + slacky && y >= event->y - slacky )
5407 params[2] = wp->image; /* we've found a match. however continue searching
5408 * since we want to find the last match -- that
5409 * is, the match that was drawn last. */
5414 static void trw_layer_show_picture ( gpointer pass_along[6] )
5416 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5418 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5421 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5422 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
5423 g_free ( quoted_file );
5424 if ( ! g_spawn_command_line_async ( cmd, &err ) )
5426 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch eog to open file.") );
5427 g_error_free ( err );
5430 #endif /* WINDOWS */
5433 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5435 gpointer params[3] = { vvp, event, NULL };
5436 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5438 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5441 static gpointer pass_along[6];
5442 pass_along[0] = vtl;
5443 pass_along[5] = params[2];
5444 trw_layer_show_picture ( pass_along );
5445 return TRUE; /* found a match */
5448 return FALSE; /* go through other layers, searching for a match */
5451 /***************************************************************************
5453 ***************************************************************************/
5459 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5461 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5462 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5465 /* Structure for thumbnail creating data used in the background thread */
5467 VikTrwLayer *vtl; // Layer needed for redrawing
5468 GSList *pics; // Image list
5469 } thumbnail_create_thread_data;
5471 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5473 guint total = g_slist_length(tctd->pics), done = 0;
5474 while ( tctd->pics )
5476 a_thumbnails_create ( (gchar *) tctd->pics->data );
5477 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5479 return -1; /* Abort thread */
5481 tctd->pics = tctd->pics->next;
5484 // Redraw to show the thumbnails as they are now created
5485 gdk_threads_enter();
5486 if ( IS_VIK_LAYER(tctd->vtl) )
5487 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) );
5488 gdk_threads_leave();
5493 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5495 while ( tctd->pics )
5497 g_free ( tctd->pics->data );
5498 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5503 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5505 if ( ! vtl->has_verified_thumbnails )
5507 GSList *pics = NULL;
5508 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5511 gint len = g_slist_length ( pics );
5512 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5513 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5516 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5518 (vik_thr_func) create_thumbnails_thread,
5520 (vik_thr_free_func) thumbnail_create_thread_free,
5528 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5530 return vtl->coord_mode;
5535 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5537 vik_coord_convert ( &(wp->coord), *dest_mode );
5540 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5542 vik_track_convert ( tr, *dest_mode );
5545 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5547 if ( vtl->coord_mode != dest_mode )
5549 vtl->coord_mode = dest_mode;
5550 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5551 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5555 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5557 return g_hash_table_lookup ( vtl->waypoints, name );
5560 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5562 return g_hash_table_lookup ( vtl->tracks, name );
5565 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5567 vtl->menu_selection = selection;
5570 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5572 return (vtl->menu_selection);
5575 /* ----------- Downloading maps along tracks --------------- */
5577 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5579 /* TODO: calculating based on current size of viewport */
5580 const gdouble w_at_zoom_0_125 = 0.0013;
5581 const gdouble h_at_zoom_0_125 = 0.0011;
5582 gdouble zoom_factor = zoom_level/0.125;
5584 wh->lat = h_at_zoom_0_125 * zoom_factor;
5585 wh->lon = w_at_zoom_0_125 * zoom_factor;
5587 return 0; /* all OK */
5590 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5592 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5593 (dist->lat >= ABS(to->north_south - from->north_south)))
5596 VikCoord *coord = g_malloc(sizeof(VikCoord));
5597 coord->mode = VIK_COORD_LATLON;
5599 if (ABS(gradient) < 1) {
5600 if (from->east_west > to->east_west)
5601 coord->east_west = from->east_west - dist->lon;
5603 coord->east_west = from->east_west + dist->lon;
5604 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5606 if (from->north_south > to->north_south)
5607 coord->north_south = from->north_south - dist->lat;
5609 coord->north_south = from->north_south + dist->lat;
5610 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5616 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5618 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5619 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5621 VikCoord *next = from;
5623 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5625 list = g_list_prepend(list, next);
5631 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5633 typedef struct _Rect {
5638 #define GLRECT(iter) ((Rect *)((iter)->data))
5641 GList *rects_to_download = NULL;
5644 if (get_download_area_width(vvp, zoom_level, &wh))
5647 GList *iter = tr->trackpoints;
5651 gboolean new_map = TRUE;
5652 VikCoord *cur_coord, tl, br;
5655 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5657 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5658 rect = g_malloc(sizeof(Rect));
5661 rect->center = *cur_coord;
5662 rects_to_download = g_list_prepend(rects_to_download, rect);
5667 gboolean found = FALSE;
5668 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5669 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
5680 GList *fillins = NULL;
5681 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
5682 /* seems that ATM the function get_next_coord works only for LATLON */
5683 if ( cur_coord->mode == VIK_COORD_LATLON ) {
5684 /* fill-ins for far apart points */
5685 GList *cur_rect, *next_rect;
5686 for (cur_rect = rects_to_download;
5687 (next_rect = cur_rect->next) != NULL;
5688 cur_rect = cur_rect->next) {
5689 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
5690 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
5691 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
5695 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
5698 GList *iter = fillins;
5700 cur_coord = (VikCoord *)(iter->data);
5701 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5702 rect = g_malloc(sizeof(Rect));
5705 rect->center = *cur_coord;
5706 rects_to_download = g_list_prepend(rects_to_download, rect);
5711 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5712 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
5716 for (iter = fillins; iter; iter = iter->next)
5718 g_list_free(fillins);
5720 if (rects_to_download) {
5721 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
5722 g_free(rect_iter->data);
5723 g_list_free(rects_to_download);
5727 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
5730 gint selected_map, default_map;
5731 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
5732 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
5733 gint selected_zoom, default_zoom;
5737 VikTrwLayer *vtl = pass_along[0];
5738 VikLayersPanel *vlp = pass_along[1];
5739 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5740 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
5742 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
5743 int num_maps = g_list_length(vmls);
5746 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
5750 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
5751 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
5753 gchar **np = map_names;
5754 VikMapsLayer **lp = map_layers;
5755 for (i = 0; i < num_maps; i++) {
5756 gboolean dup = FALSE;
5757 vml = (VikMapsLayer *)(vmls->data);
5758 for (j = 0; j < i; j++) { /* no duplicate allowed */
5759 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
5766 *np++ = vik_maps_layer_get_map_label(vml);
5772 num_maps = lp - map_layers;
5774 for (default_map = 0; default_map < num_maps; default_map++) {
5775 /* TODO: check for parent layer's visibility */
5776 if (VIK_LAYER(map_layers[default_map])->visible)
5779 default_map = (default_map == num_maps) ? 0 : default_map;
5781 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
5782 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
5783 if (cur_zoom == zoom_vals[default_zoom])
5786 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
5788 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
5791 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
5794 for (i = 0; i < num_maps; i++)
5795 g_free(map_names[i]);
5803 /**** lowest waypoint number calculation ***/
5804 static gint highest_wp_number_name_to_number(const gchar *name) {
5805 if ( strlen(name) == 3 ) {
5807 if ( n < 100 && name[0] != '0' )
5809 if ( n < 10 && name[0] != '0' )
5817 static void highest_wp_number_reset(VikTrwLayer *vtl)
5819 vtl->highest_wp_number = -1;
5822 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
5824 /* if is bigger that top, add it */
5825 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
5826 if ( new_wp_num > vtl->highest_wp_number )
5827 vtl->highest_wp_number = new_wp_num;
5830 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
5832 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
5833 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
5834 if ( vtl->highest_wp_number == old_wp_num ) {
5836 vtl->highest_wp_number --;
5838 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5839 /* search down until we find something that *does* exist */
5841 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
5842 vtl->highest_wp_number --;
5843 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5848 /* get lowest unused number */
5849 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
5852 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
5854 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
5855 return g_strdup(buf);