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) 2012, 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
25 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
26 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
33 #include "vikmapslayer.h"
34 #include "vikgpslayer.h"
35 #include "viktrwlayer_tpwin.h"
36 #include "viktrwlayer_propwin.h"
37 #ifdef VIK_CONFIG_GEOTAG
38 #include "viktrwlayer_geotag.h"
39 #include "geotag_exif.h"
41 #include "garminsymbols.h"
42 #include "thumbnails.h"
43 #include "background.h"
48 #include "geonamessearch.h"
49 #ifdef VIK_CONFIG_OPENSTREETMAP
50 #include "osm-traces.h"
53 #include "datasources.h"
54 #include "datasource_gps.h"
57 #include "icons/icons.h"
71 #include <gdk/gdkkeysyms.h>
73 #include <glib/gstdio.h>
74 #include <glib/gi18n.h>
76 #ifdef VIK_CONFIG_GOOGLE
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
80 #define VIK_TRW_LAYER_TRACK_GC 5
81 #define VIK_TRW_LAYER_TRACK_GCS 10
82 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
83 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
84 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
85 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
86 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
88 #define DRAWMODE_BY_TRACK 0
89 #define DRAWMODE_BY_SPEED 1
90 #define DRAWMODE_ALL_BLACK 2
91 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
92 // as we are (re)calculating the colour for every point
97 /* this is how it knows when you click if you are clicking close to a trackpoint. */
98 #define TRACKPOINT_SIZE_APPROX 5
99 #define WAYPOINT_SIZE_APPROX 5
101 #define MIN_STOP_LENGTH 15
102 #define MAX_STOP_LENGTH 86400
103 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
104 /* this is multiplied by user-inputted value from 1-100. */
106 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
108 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
110 FS_XX_SMALL = 0, // 'xx-small'
113 FS_MEDIUM, // DEFAULT
120 struct _VikTrwLayer {
123 GHashTable *tracks_iters;
125 GHashTable *routes_iters;
126 GHashTable *waypoints_iters;
127 GHashTable *waypoints;
128 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
129 gboolean tracks_visible, routes_visible, waypoints_visible;
132 guint8 drawpoints_size;
133 guint8 drawelevation;
134 guint8 elevation_factor;
138 guint8 drawdirections;
139 guint8 drawdirections_size;
140 guint8 line_thickness;
141 guint8 bg_line_thickness;
145 gboolean wp_draw_symbols;
146 font_size_t wp_font_size;
148 gdouble track_draw_speed_factor;
150 GdkGC *track_1color_gc;
151 GdkGC *current_track_gc;
152 // Separate GC for a track's potential new point as drawn via separate method
153 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
154 GdkGC *current_track_newpoint_gc;
157 GdkGC *waypoint_text_gc;
158 GdkGC *waypoint_bg_gc;
159 GdkFont *waypoint_font;
160 VikTrack *current_track; // ATM shared between new tracks and new routes
161 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
162 gboolean draw_sync_done;
163 gboolean draw_sync_do;
165 VikCoordMode coord_mode;
167 /* wp editing tool */
168 VikWaypoint *current_wp;
169 gpointer current_wp_id;
171 gboolean waypoint_rightclick;
173 /* track editing tool */
175 VikTrack *current_tp_track;
176 gpointer current_tp_id;
177 VikTrwLayerTpwin *tpwin;
179 /* track editing tool -- more specifically, moving tps */
182 /* route finder tool */
183 gboolean route_finder_started;
184 VikCoord route_finder_coord;
185 gboolean route_finder_check_added_track;
186 VikTrack *route_finder_added_track;
187 VikTrack *route_finder_current_track;
188 gboolean route_finder_append;
195 guint16 image_cache_size;
197 /* for waypoint text */
198 PangoLayout *wplabellayout;
200 gboolean has_verified_thumbnails;
202 GtkMenu *wp_right_click_menu;
203 GtkMenu *track_right_click_menu;
206 VikStdLayerMenuItem menu_selection;
208 gint highest_wp_number;
211 /* A caached waypoint image. */
214 gchar *image; /* filename */
217 struct DrawingParams {
221 guint16 width, height;
222 gdouble cc; // Cosine factor in track directions
223 gdouble ss; // Sine factor in track directions
224 const VikCoord *center;
225 gboolean one_zone, lat_lon;
226 gdouble ce1, ce2, cn1, cn2;
229 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
231 static void trw_layer_delete_item ( gpointer pass_along[6] );
232 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
233 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
235 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
236 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] );
237 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
239 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
240 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
242 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
243 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
245 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
246 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
247 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
248 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
249 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
250 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
251 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
252 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
253 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
254 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
255 static void trw_layer_append_track ( gpointer pass_along[6] );
256 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
257 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
258 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
259 static void trw_layer_split_segments ( gpointer pass_along[6] );
260 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
261 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
262 static void trw_layer_reverse ( gpointer pass_along[6] );
263 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
264 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
265 static void trw_layer_show_picture ( gpointer pass_along[6] );
266 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
268 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
269 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
270 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
271 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
272 static void trw_layer_new_wp ( gpointer lav[2] );
273 static void trw_layer_new_track ( gpointer lav[2] );
274 static void trw_layer_new_route ( gpointer lav[2] );
275 static void trw_layer_finish_track ( gpointer lav[2] );
276 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
277 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
278 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
279 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
280 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
281 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
282 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
283 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
284 #ifdef VIK_CONFIG_GEOTAG
285 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
286 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
287 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
288 static void trw_layer_geotagging ( gpointer lav[2] );
290 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
291 #ifdef VIK_CONFIG_GOOGLE
292 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
294 #ifdef VIK_CONFIG_OPENSTREETMAP
295 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
297 #ifdef VIK_CONFIG_GEOCACHES
298 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
300 #ifdef VIK_CONFIG_GEOTAG
301 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
303 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
304 static void trw_layer_gps_upload ( gpointer lav[2] );
306 // Specific route versions:
307 // Most track handling functions can handle operating on the route list
308 // However these ones are easier in separate functions
309 static void trw_layer_auto_routes_view ( gpointer lav[2] );
310 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
313 static void trw_layer_properties_item ( gpointer pass_along[7] );
314 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
315 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
317 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
318 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
319 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
321 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
322 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
323 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
324 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
326 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
327 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
328 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
329 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
330 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
331 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
332 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
333 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
334 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
335 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
336 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
337 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
338 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
339 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
340 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
341 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
342 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
343 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
344 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
345 #ifdef VIK_CONFIG_GOOGLE
346 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
347 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
350 static void cached_pixbuf_free ( CachedPixbuf *cp );
351 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
353 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
354 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
356 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
357 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
359 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
360 static void highest_wp_number_reset(VikTrwLayer *vtl);
361 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
362 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
364 // Note for the following tool GtkRadioActionEntry texts:
365 // the very first text value is an internal name not displayed anywhere
366 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
367 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
368 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
369 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
370 static VikToolInterface trw_layer_tools[] = {
371 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
372 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
373 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
375 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
377 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
378 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
379 (VikToolMouseFunc) tool_new_track_click,
380 (VikToolMouseMoveFunc) tool_new_track_move,
381 (VikToolMouseFunc) tool_new_track_release,
382 (VikToolKeyFunc) tool_new_track_key_press,
383 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
384 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
386 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
387 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
388 (VikToolMouseFunc) tool_new_route_click,
389 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
390 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
391 (VikToolKeyFunc) tool_new_track_key_press, // -/#
392 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
393 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
395 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
396 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
397 (VikToolMouseFunc) tool_edit_waypoint_click,
398 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
399 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
401 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
403 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
404 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
405 (VikToolMouseFunc) tool_edit_trackpoint_click,
406 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
407 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
409 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
411 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
412 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
413 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
415 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
417 #ifdef VIK_CONFIG_GOOGLE
418 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
419 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
420 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
422 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
425 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_CREATE_ROUTE, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
427 /****** PARAMETERS ******/
429 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
430 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
432 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
433 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
435 #define MIN_POINT_SIZE 2
436 #define MAX_POINT_SIZE 10
438 #define MIN_ARROW_SIZE 3
439 #define MAX_ARROW_SIZE 20
441 static VikLayerParamScale params_scales[] = {
442 /* min max step digits */
443 { 1, 10, 1, 0 }, /* line_thickness */
444 { 0, 100, 1, 0 }, /* track draw speed factor */
445 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
446 /* 5 * step == how much to turn */
447 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
448 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
449 { 5, 500, 5, 0 }, // 5: image cache_size - " "
450 { 0, 8, 1, 0 }, // 6: Background line thickness
451 { 1, 64, 1, 0 }, /* wpsize */
452 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
453 { 1, 100, 1, 0 }, // 9: elevation factor
454 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
455 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
458 static gchar* params_font_sizes[] = {
459 N_("Extra Extra Small"),
465 N_("Extra Extra Large"),
468 VikLayerParam trw_layer_params[] = {
469 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
470 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
471 { "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
473 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
474 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
475 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
476 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
477 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
478 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
479 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
480 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
481 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
483 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
484 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
486 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
487 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
488 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
490 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
491 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
492 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
493 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
494 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
495 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
496 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
497 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
498 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
500 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
501 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
502 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
503 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
506 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
508 // Sublayer visibilities
546 *** 1) Add to trw_layer_params and enumeration
547 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
550 /****** END PARAMETERS ******/
552 static VikTrwLayer* trw_layer_new ( gint drawmode );
553 /* Layer Interface function definitions */
554 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
555 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
556 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp );
557 static void trw_layer_free ( VikTrwLayer *trwlayer );
558 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
559 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
560 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
561 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
562 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
563 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
564 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
565 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
566 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
567 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
568 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
569 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
570 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
571 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
572 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
573 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
574 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
575 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
576 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
577 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
578 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
579 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
580 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
581 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
582 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
583 /* End Layer Interface function definitions */
585 VikLayerInterface vik_trw_layer_interface = {
592 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
596 params_groups, /* params_groups */
597 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
601 (VikLayerFuncCreate) trw_layer_create,
602 (VikLayerFuncRealize) trw_layer_realize,
603 (VikLayerFuncPostRead) trw_layer_post_read,
604 (VikLayerFuncFree) trw_layer_free,
606 (VikLayerFuncProperties) NULL,
607 (VikLayerFuncDraw) trw_layer_draw,
608 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
610 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
611 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
613 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
614 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
616 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
617 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
618 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
619 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
620 (VikLayerFuncLayerSelected) trw_layer_selected,
622 (VikLayerFuncMarshall) trw_layer_marshall,
623 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
625 (VikLayerFuncSetParam) trw_layer_set_param,
626 (VikLayerFuncGetParam) trw_layer_get_param,
628 (VikLayerFuncReadFileData) a_gpspoint_read_file,
629 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
631 (VikLayerFuncDeleteItem) trw_layer_del_item,
632 (VikLayerFuncCutItem) trw_layer_cut_item,
633 (VikLayerFuncCopyItem) trw_layer_copy_item,
634 (VikLayerFuncPasteItem) trw_layer_paste_item,
635 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
637 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
639 (VikLayerFuncSelectClick) trw_layer_select_click,
640 (VikLayerFuncSelectMove) trw_layer_select_move,
641 (VikLayerFuncSelectRelease) trw_layer_select_release,
642 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
645 GType vik_trw_layer_get_type ()
647 static GType vtl_type = 0;
651 static const GTypeInfo vtl_info =
653 sizeof (VikTrwLayerClass),
654 NULL, /* base_init */
655 NULL, /* base_finalize */
656 NULL, /* class init */
657 NULL, /* class_finalize */
658 NULL, /* class_data */
659 sizeof (VikTrwLayer),
661 NULL /* instance init */
663 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
669 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
671 static gpointer pass_along[6];
677 pass_along[1] = NULL;
678 pass_along[2] = GINT_TO_POINTER (subtype);
679 pass_along[3] = sublayer;
680 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
681 pass_along[5] = NULL;
683 trw_layer_delete_item ( pass_along );
686 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
688 static gpointer pass_along[6];
694 pass_along[1] = NULL;
695 pass_along[2] = GINT_TO_POINTER (subtype);
696 pass_along[3] = sublayer;
697 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
698 pass_along[5] = NULL;
700 trw_layer_copy_item_cb(pass_along);
701 trw_layer_cut_item_cb(pass_along);
704 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
706 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
707 gint subtype = GPOINTER_TO_INT (pass_along[2]);
708 gpointer * sublayer = pass_along[3];
712 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
716 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
717 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
718 if ( wp && wp->name )
721 name = NULL; // Broken :(
723 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
724 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
725 if ( trk && trk->name )
728 name = NULL; // Broken :(
731 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
732 if ( trk && trk->name )
735 name = NULL; // Broken :(
738 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
739 subtype, len, name, data);
743 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
745 trw_layer_copy_item_cb(pass_along);
746 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
747 trw_layer_delete_item(pass_along);
750 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
752 // Slightly cheating method, routing via the panels capability
753 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
756 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
766 GByteArray *ba = g_byte_array_new ();
768 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
769 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
770 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
771 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
773 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
776 g_byte_array_append ( ba, id, il );
784 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
791 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
795 w = vik_waypoint_unmarshall ( item, len );
796 // When copying - we'll create a new name based on the original
797 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
798 vik_trw_layer_add_waypoint ( vtl, name, w );
799 waypoint_convert (NULL, w, &vtl->coord_mode);
801 // Consider if redraw necessary for the new item
802 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
803 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
806 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
810 t = vik_track_unmarshall ( item, len );
811 // When copying - we'll create a new name based on the original
812 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
813 vik_trw_layer_add_track ( vtl, name, t );
814 vik_track_convert (t, vtl->coord_mode);
816 // Consider if redraw necessary for the new item
817 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
818 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
821 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
825 t = vik_track_unmarshall ( item, len );
826 // When copying - we'll create a new name based on the original
827 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
828 vik_trw_layer_add_route ( vtl, name, t );
829 vik_track_convert (t, vtl->coord_mode);
831 // Consider if redraw necessary for the new item
832 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
833 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
839 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
846 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
850 case PARAM_TV: vtl->tracks_visible = data.b; break;
851 case PARAM_WV: vtl->waypoints_visible = data.b; break;
852 case PARAM_RV: vtl->routes_visible = data.b; break;
853 case PARAM_DM: vtl->drawmode = data.u; break;
854 case PARAM_DP: vtl->drawpoints = data.b; break;
856 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
857 vtl->drawpoints_size = data.u;
859 case PARAM_DE: vtl->drawelevation = data.b; break;
860 case PARAM_DS: vtl->drawstops = data.b; break;
861 case PARAM_DL: vtl->drawlines = data.b; break;
862 case PARAM_DD: vtl->drawdirections = data.b; break;
864 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
865 vtl->drawdirections_size = data.u;
867 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
868 vtl->stop_length = data.u;
870 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
871 vtl->elevation_factor = data.u;
873 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
875 vtl->line_thickness = data.u;
876 trw_layer_new_track_gcs ( vtl, vp );
879 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
881 vtl->bg_line_thickness = data.u;
882 trw_layer_new_track_gcs ( vtl, vp );
885 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
886 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
887 case PARAM_DLA: vtl->drawlabels = data.b; break;
888 case PARAM_DI: vtl->drawimages = data.b; break;
889 case PARAM_IS: if ( data.u != vtl->image_size )
891 vtl->image_size = data.u;
892 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
893 g_queue_free ( vtl->image_cache );
894 vtl->image_cache = g_queue_new ();
897 case PARAM_IA: vtl->image_alpha = data.u; break;
898 case PARAM_ICS: vtl->image_cache_size = data.u;
899 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
900 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
902 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
903 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
904 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
905 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
906 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
907 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
908 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
909 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
914 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
916 VikLayerParamData rv;
919 case PARAM_TV: rv.b = vtl->tracks_visible; break;
920 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
921 case PARAM_RV: rv.b = vtl->routes_visible; break;
922 case PARAM_DM: rv.u = vtl->drawmode; break;
923 case PARAM_DP: rv.b = vtl->drawpoints; break;
924 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
925 case PARAM_DE: rv.b = vtl->drawelevation; break;
926 case PARAM_EF: rv.u = vtl->elevation_factor; break;
927 case PARAM_DS: rv.b = vtl->drawstops; break;
928 case PARAM_SL: rv.u = vtl->stop_length; break;
929 case PARAM_DL: rv.b = vtl->drawlines; break;
930 case PARAM_DD: rv.b = vtl->drawdirections; break;
931 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
932 case PARAM_LT: rv.u = vtl->line_thickness; break;
933 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
934 case PARAM_DLA: rv.b = vtl->drawlabels; break;
935 case PARAM_DI: rv.b = vtl->drawimages; break;
936 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
937 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
938 case PARAM_IS: rv.u = vtl->image_size; break;
939 case PARAM_IA: rv.u = vtl->image_alpha; break;
940 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
941 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
942 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
943 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
944 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
945 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
946 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
947 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
948 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
953 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
960 // Use byte arrays to store sublayer data
961 // much like done elsewhere e.g. vik_layer_marshall_params()
962 GByteArray *ba = g_byte_array_new ( );
970 // the length of the item
971 // the sublayer type of item
972 // the the actual item
973 #define tlm_append(object_pointer, size, type) \
975 object_length = (size); \
976 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
977 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
978 g_byte_array_append ( ba, (object_pointer), object_length );
980 // Layer parameters first
981 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
982 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
983 g_byte_array_append ( ba, pd, pl );
991 g_hash_table_iter_init ( &iter, vtl->waypoints );
992 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
993 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
994 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
999 g_hash_table_iter_init ( &iter, vtl->tracks );
1000 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1001 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1002 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1007 g_hash_table_iter_init ( &iter, vtl->routes );
1008 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1009 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1010 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1020 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1022 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1024 gint consumed_length;
1026 // First the overall layer parameters
1027 memcpy(&pl, data, sizeof(pl));
1029 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1032 consumed_length = pl;
1033 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1035 #define tlm_size (*(gint *)data)
1036 // See marshalling above for order of how this is written
1038 data += sizeof_len_and_subtype + tlm_size;
1040 // Now the individual sublayers:
1042 while ( *data && consumed_length < len ) {
1043 // Normally four extra bytes at the end of the datastream
1044 // (since it's a GByteArray and that's where it's length is stored)
1045 // So only attempt read when there's an actual block of sublayer data
1046 if ( consumed_length + tlm_size < len ) {
1048 // Reuse pl to read the subtype from the data stream
1049 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1051 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1052 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1053 gchar *name = g_strdup ( trk->name );
1054 vik_trw_layer_add_track ( vtl, name, trk );
1057 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1058 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1059 gchar *name = g_strdup ( wp->name );
1060 vik_trw_layer_add_waypoint ( vtl, name, wp );
1063 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1064 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1065 gchar *name = g_strdup ( trk->name );
1066 vik_trw_layer_add_route ( vtl, name, trk );
1070 consumed_length += tlm_size + sizeof_len_and_subtype;
1073 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1078 // Keep interesting hash function at least visible
1080 static guint strcase_hash(gconstpointer v)
1082 // 31 bit hash function
1085 gchar s[128]; // malloc is too slow for reading big files
1088 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1089 p[i] = toupper(t[i]);
1095 for (p += 1; *p != '\0'; p++)
1096 h = (h << 5) - h + *p;
1103 static VikTrwLayer* trw_layer_new ( gint drawmode )
1105 if (trw_layer_params[PARAM_DM].widget_data == NULL)
1106 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
1107 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
1108 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
1110 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1111 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1113 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1114 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1116 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1117 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1118 // and with normal PC processing capabilities - it has negligibile performance impact
1119 // This also minimized the amount of rework - as the management of the hash tables already exists.
1121 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1122 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1123 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1125 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1126 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1127 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1128 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1129 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1130 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1133 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1134 rv->drawmode = drawmode;
1135 rv->drawpoints = TRUE;
1136 rv->drawpoints_size = MIN_POINT_SIZE;
1137 rv->drawdirections_size = 5;
1138 rv->elevation_factor = 30;
1139 rv->stop_length = 60;
1140 rv->drawlines = TRUE;
1141 rv->track_draw_speed_factor = 30.0;
1142 rv->line_thickness = 1;
1144 rv->draw_sync_done = TRUE;
1145 rv->draw_sync_do = TRUE;
1147 rv->image_cache = g_queue_new();
1148 rv->image_size = 64;
1149 rv->image_alpha = 255;
1150 rv->image_cache_size = 300;
1151 rv->drawimages = TRUE;
1152 rv->drawlabels = TRUE;
1153 // Everything else is 0, FALSE or NULL
1159 static void trw_layer_free ( VikTrwLayer *trwlayer )
1161 g_hash_table_destroy(trwlayer->waypoints);
1162 g_hash_table_destroy(trwlayer->tracks);
1164 /* ODC: replace with GArray */
1165 trw_layer_free_track_gcs ( trwlayer );
1167 if ( trwlayer->wp_right_click_menu )
1168 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1170 if ( trwlayer->track_right_click_menu )
1171 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1173 if ( trwlayer->wplabellayout != NULL)
1174 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1176 if ( trwlayer->waypoint_gc != NULL )
1177 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1179 if ( trwlayer->waypoint_text_gc != NULL )
1180 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1182 if ( trwlayer->waypoint_bg_gc != NULL )
1183 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1185 if ( trwlayer->tpwin != NULL )
1186 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1188 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1189 g_queue_free ( trwlayer->image_cache );
1192 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1196 dp->xmpp = vik_viewport_get_xmpp ( vp );
1197 dp->ympp = vik_viewport_get_ympp ( vp );
1198 dp->width = vik_viewport_get_width ( vp );
1199 dp->height = vik_viewport_get_height ( vp );
1200 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1201 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1203 dp->center = vik_viewport_get_center ( vp );
1204 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1205 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1210 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1211 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1212 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1214 dp->ce1 = dp->center->east_west-w2;
1215 dp->ce2 = dp->center->east_west+w2;
1216 dp->cn1 = dp->center->north_south-h2;
1217 dp->cn2 = dp->center->north_south+h2;
1218 } else if ( dp->lat_lon ) {
1219 VikCoord upperleft, bottomright;
1220 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1221 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1222 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1223 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1224 dp->ce1 = upperleft.east_west;
1225 dp->ce2 = bottomright.east_west;
1226 dp->cn1 = bottomright.north_south;
1227 dp->cn2 = upperleft.north_south;
1232 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1233 * Here a simple traffic like light colour system is used:
1234 * . slow points are red
1235 * . average is yellow
1236 * . fast points are green
1238 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1241 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1242 if ( average_speed > 0 ) {
1243 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1244 if ( rv < low_speed )
1245 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1246 else if ( rv > high_speed )
1247 return VIK_TRW_LAYER_TRACK_GC_FAST;
1249 return VIK_TRW_LAYER_TRACK_GC_AVER;
1252 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1255 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1257 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1258 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1259 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1260 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1263 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1265 /* TODO: this function is a mess, get rid of any redundancy */
1266 GList *list = track->trackpoints;
1268 gboolean useoldvals = TRUE;
1270 gboolean drawpoints;
1272 gboolean drawelevation;
1273 gdouble min_alt, max_alt, alt_diff = 0;
1275 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1276 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1279 if ( dp->vtl->drawelevation )
1281 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1282 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1283 alt_diff = max_alt - min_alt;
1286 if ( ! track->visible )
1289 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1290 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1291 trw_layer_draw_track ( name, track, dp, TRUE );
1293 if ( draw_track_outline )
1294 drawpoints = drawstops = FALSE;
1296 drawpoints = dp->vtl->drawpoints;
1297 drawstops = dp->vtl->drawstops;
1300 gboolean drawing_highlight = FALSE;
1301 /* Current track - used for creation */
1302 if ( track == dp->vtl->current_track )
1303 main_gc = dp->vtl->current_track_gc;
1305 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1306 /* Draw all tracks of the layer in special colour */
1307 /* if track is member of selected layer or is the current selected track
1308 then draw in the highlight colour.
1309 NB this supercedes the drawmode */
1310 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1311 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) ||
1312 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) ||
1313 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1314 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1315 drawing_highlight = TRUE;
1318 if ( !drawing_highlight ) {
1319 // Still need to figure out the gc according to the drawing mode:
1320 switch ( dp->vtl->drawmode ) {
1321 case DRAWMODE_BY_TRACK:
1322 if ( dp->vtl->track_1color_gc )
1323 g_object_unref ( dp->vtl->track_1color_gc );
1324 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1325 main_gc = dp->vtl->track_1color_gc;
1328 // Mostly for DRAWMODE_ALL_BLACK
1329 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1330 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_BLACK);
1337 int x, y, oldx, oldy;
1338 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1340 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1342 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1344 // Draw the first point as something a bit different from the normal points
1345 // ATM it's slightly bigger and a triangle
1347 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1348 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1354 gdouble average_speed = 0.0;
1355 gdouble low_speed = 0.0;
1356 gdouble high_speed = 0.0;
1357 // If necessary calculate these values - which is done only once per track redraw
1358 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1359 // the percentage factor away from the average speed determines transistions between the levels
1360 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1361 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1362 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1365 while ((list = g_list_next(list)))
1367 tp = VIK_TRACKPOINT(list->data);
1368 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1370 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1371 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1372 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1373 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1374 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1376 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1379 * If points are the same in display coordinates, don't draw.
1381 if ( useoldvals && x == oldx && y == oldy )
1383 // Still need to process points to ensure 'stops' are drawn if required
1384 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1385 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1386 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 );
1391 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1392 if ( drawpoints || dp->vtl->drawlines ) {
1393 // setup main_gc for both point and line drawing
1394 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1395 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
1399 if ( drawpoints && ! draw_track_outline )
1404 * The concept of drawing stops is that a trackpoint
1405 * that is if the next trackpoint has a timestamp far into
1406 * the future, we draw a circle of 6x trackpoint size,
1407 * instead of a rectangle of 2x trackpoint size.
1408 * This is drawn first so the trackpoint will be drawn on top
1411 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1412 /* Stop point. Draw 6x circle. Always in redish colour */
1413 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
1415 /* Regular point - draw 2x square. */
1416 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1419 /* Final point - draw 4x circle. */
1420 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 );
1423 if ((!tp->newsegment) && (dp->vtl->drawlines))
1426 /* UTM only: zone check */
1427 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1428 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1431 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1433 if ( draw_track_outline ) {
1434 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1438 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1440 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1442 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1447 tmp[1].y = oldy-FIXALTITUDE(list->data);
1449 tmp[2].y = y-FIXALTITUDE(list->next->data);
1454 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1455 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1457 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1458 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1460 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1465 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1466 // Draw an arrow at the mid point to show the direction of the track
1467 // Code is a rework from vikwindow::draw_ruler()
1468 gint midx = (oldx + x) / 2;
1469 gint midy = (oldy + y) / 2;
1471 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1472 // Avoid divide by zero and ensure at least 1 pixel big
1474 gdouble dx = (oldx - midx) / len;
1475 gdouble dy = (oldy - midy) / len;
1476 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1477 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1487 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1489 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1490 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1492 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1494 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1495 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
1499 * If points are the same in display coordinates, don't draw.
1501 if ( x != oldx || y != oldy )
1503 if ( draw_track_outline )
1504 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1506 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1512 * If points are the same in display coordinates, don't draw.
1514 if ( x != oldx && y != oldy )
1516 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1517 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1527 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1528 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1530 trw_layer_draw_track ( name, track, dp, FALSE );
1533 static void cached_pixbuf_free ( CachedPixbuf *cp )
1535 g_object_unref ( G_OBJECT(cp->pixbuf) );
1536 g_free ( cp->image );
1539 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1541 return strcmp ( cp->image, name );
1544 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1547 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1548 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1549 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1552 GdkPixbuf *sym = NULL;
1553 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1555 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1557 if ( wp->image && dp->vtl->drawimages )
1559 GdkPixbuf *pixbuf = NULL;
1562 if ( dp->vtl->image_alpha == 0)
1565 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1567 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1570 gchar *image = wp->image;
1571 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1572 if ( ! regularthumb )
1574 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1575 image = "\x12\x00"; /* this shouldn't occur naturally. */
1579 CachedPixbuf *cp = NULL;
1580 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1581 if ( dp->vtl->image_size == 128 )
1582 cp->pixbuf = regularthumb;
1585 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1586 g_assert ( cp->pixbuf );
1587 g_object_unref ( G_OBJECT(regularthumb) );
1589 cp->image = g_strdup ( image );
1591 /* needed so 'click picture' tool knows how big the pic is; we don't
1592 * store it in cp because they may have been freed already. */
1593 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1594 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1596 g_queue_push_head ( dp->vtl->image_cache, cp );
1597 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1598 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1600 pixbuf = cp->pixbuf;
1604 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1610 w = gdk_pixbuf_get_width ( pixbuf );
1611 h = gdk_pixbuf_get_height ( pixbuf );
1613 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1615 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1616 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1617 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1618 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1619 // Highlighted - so draw a little border around the chosen one
1620 // single line seems a little weak so draw 2 of them
1621 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1622 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1623 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1624 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1627 if ( dp->vtl->image_alpha == 255 )
1628 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1630 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1632 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1636 /* DRAW ACTUAL DOT */
1637 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1638 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 );
1640 else if ( wp == dp->vtl->current_wp ) {
1641 switch ( dp->vtl->wp_symbol ) {
1642 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;
1643 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;
1644 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;
1645 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 );
1646 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 );
1650 switch ( dp->vtl->wp_symbol ) {
1651 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;
1652 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;
1653 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;
1654 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 );
1655 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;
1659 if ( dp->vtl->drawlabels )
1661 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1662 gint label_x, label_y;
1664 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1666 // Could this stored in the waypoint rather than recreating each pass?
1667 gchar *fsize = NULL;
1668 switch (dp->vtl->wp_font_size) {
1669 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1670 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1671 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1672 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1673 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1674 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1675 default: fsize = g_strdup ( "medium" ); break;
1678 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1680 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1681 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1683 // Fallback if parse failure
1684 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1686 g_free ( wp_label_markup );
1689 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1690 label_x = x - width/2;
1692 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1694 label_y = y - dp->vtl->wp_size - height - 2;
1696 /* if highlight mode on, then draw background text in highlight colour */
1697 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1698 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1699 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1700 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1701 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1703 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1706 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1708 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1713 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1715 static struct DrawingParams dp;
1716 g_assert ( l != NULL );
1718 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1720 if ( l->tracks_visible )
1721 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1723 if ( l->routes_visible )
1724 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1726 if (l->waypoints_visible)
1727 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1730 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1733 if ( vtl->track_bg_gc )
1735 g_object_unref ( vtl->track_bg_gc );
1736 vtl->track_bg_gc = NULL;
1738 if ( vtl->track_1color_gc )
1740 g_object_unref ( vtl->track_1color_gc );
1741 vtl->track_1color_gc = NULL;
1743 if ( vtl->current_track_gc )
1745 g_object_unref ( vtl->current_track_gc );
1746 vtl->current_track_gc = NULL;
1748 if ( vtl->current_track_newpoint_gc )
1750 g_object_unref ( vtl->current_track_newpoint_gc );
1751 vtl->current_track_newpoint_gc = NULL;
1754 if ( ! vtl->track_gc )
1756 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1757 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1758 g_array_free ( vtl->track_gc, TRUE );
1759 vtl->track_gc = NULL;
1762 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1764 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1765 gint width = vtl->line_thickness;
1767 if ( vtl->track_gc )
1768 trw_layer_free_track_gcs ( vtl );
1770 if ( vtl->track_bg_gc )
1771 g_object_unref ( vtl->track_bg_gc );
1772 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1774 // Ensure new track drawing heeds line thickness setting
1775 // however always have a minium of 2, as 1 pixel is really narrow
1776 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1778 if ( vtl->current_track_gc )
1779 g_object_unref ( vtl->current_track_gc );
1780 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1781 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1783 // 'newpoint' gc is exactly the same as the current track gc
1784 if ( vtl->current_track_newpoint_gc )
1785 g_object_unref ( vtl->current_track_newpoint_gc );
1786 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1787 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1789 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1791 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1792 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1794 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1795 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1796 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1798 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1801 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1803 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1804 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1806 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1807 /* early exit, as the rest is GUI related */
1811 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1812 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1814 trw_layer_new_track_gcs ( rv, vp );
1816 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1817 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1818 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1819 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1821 rv->has_verified_thumbnails = FALSE;
1822 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1824 rv->wp_draw_symbols = TRUE;
1825 rv->wp_font_size = FS_MEDIUM;
1827 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1829 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1834 #define SMALL_ICON_SIZE 18
1836 * Can accept a null symbol, and may return null value
1838 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1840 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1841 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1842 // So needing a small icon for the treeview may need some resizing:
1843 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1844 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1848 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1850 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1852 GdkPixbuf *pixbuf = NULL;
1854 if ( track->has_color ) {
1855 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1856 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1857 // Here is some magic found to do the conversion
1858 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1859 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1860 ((track->color.green & 0xff00) << 8) |
1861 (track->color.blue & 0xff00);
1863 gdk_pixbuf_fill ( pixbuf, pixel );
1866 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1867 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
1869 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
1873 g_object_unref (pixbuf);
1875 *new_iter = *((GtkTreeIter *) pass_along[1]);
1876 if ( track->is_route )
1877 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1879 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1881 if ( ! track->visible )
1882 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1885 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1887 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1889 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1890 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
1892 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
1895 *new_iter = *((GtkTreeIter *) pass_along[1]);
1896 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1898 if ( ! wp->visible )
1899 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1902 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1904 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1905 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1907 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1911 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1913 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1914 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1916 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1920 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1922 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1923 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1925 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1929 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1932 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1934 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1935 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1936 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1938 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1941 if ( g_hash_table_size (vtl->routes) > 0 ) {
1943 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
1945 pass_along[0] = &(vtl->routes_iter);
1946 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
1948 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
1950 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
1953 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
1954 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
1956 pass_along[0] = &(vtl->waypoints_iter);
1957 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1959 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1961 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1966 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1970 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1971 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1972 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
1973 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1975 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1977 return (t->visible ^= 1);
1981 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1983 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1985 return (t->visible ^= 1);
1989 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
1991 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
1993 return (t->visible ^= 1);
2002 * Return a property about tracks for this layer
2004 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2006 return vtl->line_thickness;
2009 // Structure to hold multiple track information for a layer
2018 * Build up layer multiple track information via updating the tooltip_tracks structure
2020 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2022 tt->length = tt->length + vik_track_get_length (tr);
2024 // Ensure times are available
2025 if ( tr->trackpoints &&
2026 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2027 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2030 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2031 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2033 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2034 // Hence initialize to the first 'proper' value
2035 if ( tt->start_time == 0 )
2036 tt->start_time = t1;
2037 if ( tt->end_time == 0 )
2040 // Update find the earliest / last times
2041 if ( t1 < tt->start_time )
2042 tt->start_time = t1;
2043 if ( t2 > tt->end_time )
2046 // Keep track of total time
2047 // there maybe gaps within a track (eg segments)
2048 // but this should be generally good enough for a simple indicator
2049 tt->duration = tt->duration + (int)(t2-t1);
2054 * Generate tooltip text for the layer.
2055 * This is relatively complicated as it considers information for
2056 * no tracks, a single track or multiple tracks
2057 * (which may or may not have timing information)
2059 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2070 static gchar tmp_buf[128];
2073 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2075 // Safety check - I think these should always be valid
2076 if ( vtl->tracks && vtl->waypoints ) {
2077 tooltip_tracks tt = { 0.0, 0, 0 };
2078 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2080 GDate* gdate_start = g_date_new ();
2081 g_date_set_time_t (gdate_start, tt.start_time);
2083 GDate* gdate_end = g_date_new ();
2084 g_date_set_time_t (gdate_end, tt.end_time);
2086 if ( g_date_compare (gdate_start, gdate_end) ) {
2087 // Dates differ so print range on separate line
2088 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2089 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2090 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2093 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2094 if ( tt.start_time != 0 )
2095 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2099 if ( tt.length > 0.0 ) {
2100 gdouble len_in_units;
2102 // Setup info dependent on distance units
2103 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2104 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2105 len_in_units = VIK_METERS_TO_MILES(tt.length);
2108 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2109 len_in_units = tt.length/1000.0;
2112 // Timing information if available
2114 if ( tt.duration > 0 ) {
2115 g_snprintf (tbuf1, sizeof(tbuf1),
2116 _(" in %d:%02d hrs:mins"),
2117 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2119 g_snprintf (tbuf2, sizeof(tbuf2),
2120 _("\n%sTotal Length %.1f %s%s"),
2121 tbuf3, len_in_units, tbuf4, tbuf1);
2124 // Put together all the elements to form compact tooltip text
2125 g_snprintf (tmp_buf, sizeof(tmp_buf),
2126 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2127 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2129 g_date_free (gdate_start);
2130 g_date_free (gdate_end);
2137 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2141 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2143 // Very simple tooltip - may expand detail in the future...
2144 static gchar tmp_buf[32];
2145 g_snprintf (tmp_buf, sizeof(tmp_buf),
2147 g_hash_table_size (l->tracks));
2151 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2153 // Very simple tooltip - may expand detail in the future...
2154 static gchar tmp_buf[32];
2155 g_snprintf (tmp_buf, sizeof(tmp_buf),
2157 g_hash_table_size (l->routes));
2162 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2163 // Same tooltip for a route
2164 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2167 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2168 tr = g_hash_table_lookup ( l->tracks, sublayer );
2170 tr = g_hash_table_lookup ( l->routes, sublayer );
2173 // Could be a better way of handling strings - but this works...
2174 gchar time_buf1[20];
2175 gchar time_buf2[20];
2176 time_buf1[0] = '\0';
2177 time_buf2[0] = '\0';
2178 static gchar tmp_buf[100];
2179 // Compact info: Short date eg (11/20/99), duration and length
2180 // Hopefully these are the things that are most useful and so promoted into the tooltip
2181 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2182 // %x The preferred date representation for the current locale without the time.
2183 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2184 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2185 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2187 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2190 // Get length and consider the appropriate distance units
2191 gdouble tr_len = vik_track_get_length(tr);
2192 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2193 switch (dist_units) {
2194 case VIK_UNITS_DISTANCE_KILOMETRES:
2195 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2197 case VIK_UNITS_DISTANCE_MILES:
2198 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2207 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2209 // Very simple tooltip - may expand detail in the future...
2210 static gchar tmp_buf[32];
2211 g_snprintf (tmp_buf, sizeof(tmp_buf),
2213 g_hash_table_size (l->waypoints));
2217 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2219 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2220 // NB It's OK to return NULL
2231 * Function to show basic track point information on the statusbar
2233 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2236 switch (a_vik_get_units_height ()) {
2237 case VIK_UNITS_HEIGHT_FEET:
2238 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2241 //VIK_UNITS_HEIGHT_METRES:
2242 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2247 if ( trkpt->has_timestamp ) {
2248 // Compact date time format
2249 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2253 // Position is put later on, as this bit may not be seen if the display is not big enough,
2254 // one can easily use the current pointer position to see this if needed
2255 gchar *lat = NULL, *lon = NULL;
2256 static struct LatLon ll;
2257 vik_coord_to_latlon (&(trkpt->coord), &ll);
2258 a_coords_latlon_to_string ( &ll, &lat, &lon );
2261 // Again is put later on, as this bit may not be seen if the display is not big enough
2262 // trackname can be seen from the treeview (when enabled)
2263 // Also name could be very long to not leave room for anything else
2266 if ( vtl->current_tp_track ) {
2267 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2270 // Combine parts to make overall message
2271 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2272 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2279 * Function to show basic waypoint information on the statusbar
2281 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2284 switch (a_vik_get_units_height ()) {
2285 case VIK_UNITS_HEIGHT_FEET:
2286 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2289 //VIK_UNITS_HEIGHT_METRES:
2290 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2294 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2295 // one can easily use the current pointer position to see this if needed
2296 gchar *lat = NULL, *lon = NULL;
2297 static struct LatLon ll;
2298 vik_coord_to_latlon (&(wpt->coord), &ll);
2299 a_coords_latlon_to_string ( &ll, &lat, &lon );
2301 // Combine parts to make overall message
2304 // Add comment if available
2305 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2307 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2308 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2315 * General layer selection function, find out which bit is selected and take appropriate action
2317 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2320 l->current_wp = NULL;
2321 l->current_wp_id = NULL;
2322 trw_layer_cancel_current_tp ( l, FALSE );
2325 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2329 case VIK_TREEVIEW_TYPE_LAYER:
2331 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2332 /* Mark for redraw */
2337 case VIK_TREEVIEW_TYPE_SUBLAYER:
2341 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2343 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2344 /* Mark for redraw */
2348 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2350 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2351 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2352 /* Mark for redraw */
2356 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2358 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2359 /* Mark for redraw */
2363 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2365 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2366 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2367 /* Mark for redraw */
2371 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2373 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2374 /* Mark for redraw */
2378 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2380 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2382 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2383 // Show some waypoint info
2384 set_statusbar_msg_info_wpt ( l, wpt );
2385 /* Mark for redraw */
2392 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2401 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2406 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2411 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2416 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2418 return l->waypoints;
2422 * ATM use a case sensitive find
2423 * Finds the first one
2425 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2427 if ( wp && wp->name )
2428 if ( ! strcmp ( wp->name, name ) )
2434 * Get waypoint by name - not guaranteed to be unique
2435 * Finds the first one
2437 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2439 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2443 * ATM use a case sensitive find
2444 * Finds the first one
2446 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2448 if ( trk && trk->name )
2449 if ( ! strcmp ( trk->name, name ) )
2455 * Get track by name - not guaranteed to be unique
2456 * Finds the first one
2458 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2460 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2464 * Get route by name - not guaranteed to be unique
2465 * Finds the first one
2467 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2469 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2472 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2474 static VikCoord fixme;
2475 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2476 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2477 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2478 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2479 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2480 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2481 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2482 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2483 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2486 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2488 GList *tr = trk->trackpoints;
2489 static VikCoord fixme;
2493 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2494 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2495 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2496 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2497 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2498 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2499 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2500 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2501 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2506 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2508 // Continually reuse maxmin to find the latest maximum and minimum values
2509 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2510 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2511 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2514 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2516 /* 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... */
2517 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2518 trw_layer_find_maxmin (vtl, maxmin);
2519 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2523 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2524 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2529 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2532 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2533 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2535 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2538 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2540 /* First set the center [in case previously viewing from elsewhere] */
2541 /* Then loop through zoom levels until provided positions are in view */
2542 /* This method is not particularly fast - but should work well enough */
2543 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2545 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2546 vik_viewport_set_center_coord ( vvp, &coord );
2548 /* Convert into definite 'smallest' and 'largest' positions */
2549 struct LatLon minmin;
2550 if ( maxmin[0].lat < maxmin[1].lat )
2551 minmin.lat = maxmin[0].lat;
2553 minmin.lat = maxmin[1].lat;
2555 struct LatLon maxmax;
2556 if ( maxmin[0].lon > maxmin[1].lon )
2557 maxmax.lon = maxmin[0].lon;
2559 maxmax.lon = maxmin[1].lon;
2561 /* Never zoom in too far - generally not that useful, as too close ! */
2562 /* Always recalculate the 'best' zoom level */
2564 vik_viewport_set_zoom ( vvp, zoom );
2566 gdouble min_lat, max_lat, min_lon, max_lon;
2567 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2568 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2569 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2570 /* NB I think the logic used in this test to determine if the bounds is within view
2571 fails if track goes across 180 degrees longitude.
2572 Hopefully that situation is not too common...
2573 Mind you viking doesn't really do edge locations to well anyway */
2574 if ( min_lat < minmin.lat &&
2575 max_lat > minmin.lat &&
2576 min_lon < maxmax.lon &&
2577 max_lon > maxmax.lon )
2578 /* Found within zoom level */
2583 vik_viewport_set_zoom ( vvp, zoom );
2587 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2589 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2590 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2591 trw_layer_find_maxmin (vtl, maxmin);
2592 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2595 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2600 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2602 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])) ) ) {
2603 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2606 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2609 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2611 GtkWidget *file_selector;
2613 gboolean failed = FALSE;
2614 file_selector = gtk_file_chooser_dialog_new (title,
2616 GTK_FILE_CHOOSER_ACTION_SAVE,
2617 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2618 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2620 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2622 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2624 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2625 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2627 gtk_widget_hide ( file_selector );
2628 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2633 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2635 gtk_widget_hide ( file_selector );
2636 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2641 gtk_widget_destroy ( file_selector );
2643 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2646 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2648 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2651 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2653 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2656 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2658 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2659 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2660 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2661 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2663 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2665 g_free ( auto_save_name );
2668 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2670 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2671 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2672 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2673 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2675 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2677 g_free ( auto_save_name );
2681 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2684 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2686 gchar *name_used = NULL;
2689 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2690 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2692 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2696 gchar *quoted_file = g_shell_quote ( name_used );
2697 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2698 g_free ( quoted_file );
2699 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2701 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2702 g_error_free ( err );
2706 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2707 //g_remove ( name_used );
2708 // Perhaps should be deleted when the program ends?
2709 // For now leave it to the user to delete it / use system temp cleanup methods.
2710 g_free ( name_used );
2714 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2716 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2719 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2721 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2724 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2726 gpointer layer_and_vlp[2];
2727 layer_and_vlp[0] = pass_along[0];
2728 layer_and_vlp[1] = pass_along[1];
2730 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2732 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2733 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2735 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2737 if ( !trk || !trk->name )
2740 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2741 gchar *auto_save_name = g_strdup ( trk->name );
2742 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2743 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2745 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2747 g_free ( auto_save_name );
2751 VikWaypoint *wp; // input
2752 gpointer uuid; // output
2755 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2757 wpu_udata *user_data = udata;
2758 if ( wp == user_data->wp ) {
2759 user_data->uuid = id;
2765 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2767 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2768 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2769 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2771 GTK_RESPONSE_REJECT,
2773 GTK_RESPONSE_ACCEPT,
2776 GtkWidget *label, *entry;
2777 label = gtk_label_new(_("Waypoint Name:"));
2778 entry = gtk_entry_new();
2780 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2781 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2782 gtk_widget_show_all ( label );
2783 gtk_widget_show_all ( entry );
2785 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2787 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2789 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2790 // Find *first* wp with the given name
2791 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2794 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2797 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2798 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2800 // Find and select on the side panel
2805 // Hmmm, want key of it
2806 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2808 if ( wpf && udata.uuid ) {
2809 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2810 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2819 gtk_widget_destroy ( dia );
2822 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2824 gchar *default_name = highest_wp_number_get(vtl);
2825 VikWaypoint *wp = vik_waypoint_new();
2826 gchar *returned_name;
2828 wp->coord = *def_coord;
2830 // Attempt to auto set height if DEM data is available
2831 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2832 if ( elev != VIK_DEM_INVALID_ELEVATION )
2833 wp->altitude = (gdouble)elev;
2835 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2837 if ( returned_name )
2840 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2841 g_free (default_name);
2842 g_free (returned_name);
2845 g_free (default_name);
2846 vik_waypoint_free(wp);
2850 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2853 struct LatLon one_ll, two_ll;
2854 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2856 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2857 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2858 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2859 VikViewport *vvp = vik_window_viewport(vw);
2860 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2861 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2862 vik_coord_to_latlon(&one, &one_ll);
2863 vik_coord_to_latlon(&two, &two_ll);
2864 if (one_ll.lat > two_ll.lat) {
2865 maxmin[0].lat = one_ll.lat;
2866 maxmin[1].lat = two_ll.lat;
2869 maxmin[0].lat = two_ll.lat;
2870 maxmin[1].lat = one_ll.lat;
2872 if (one_ll.lon > two_ll.lon) {
2873 maxmin[0].lon = one_ll.lon;
2874 maxmin[1].lon = two_ll.lon;
2877 maxmin[0].lon = two_ll.lon;
2878 maxmin[1].lon = one_ll.lon;
2880 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2883 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2885 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2886 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2887 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2889 trw_layer_find_maxmin (vtl, maxmin);
2890 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2893 #ifdef VIK_CONFIG_GEOTAG
2894 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2896 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2898 // Update directly - not changing the mtime
2899 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2902 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2904 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2907 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2911 * Use code in separate file for this feature as reasonably complex
2913 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2915 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2916 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2917 // Unset so can be reverified later if necessary
2918 vtl->has_verified_thumbnails = FALSE;
2920 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2926 static void trw_layer_geotagging ( gpointer lav[2] )
2928 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2929 // Unset so can be reverified later if necessary
2930 vtl->has_verified_thumbnails = FALSE;
2932 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2939 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2942 * Acquire into this TRW Layer straight from GPS Device
2944 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2946 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2947 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2948 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2949 VikViewport *vvp = vik_window_viewport(vw);
2951 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2952 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2955 #ifdef VIK_CONFIG_GOOGLE
2957 * Acquire into this TRW Layer from Google Directions
2959 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2961 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2962 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2963 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2964 VikViewport *vvp = vik_window_viewport(vw);
2966 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2970 #ifdef VIK_CONFIG_OPENSTREETMAP
2972 * Acquire into this TRW Layer from OSM
2974 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2976 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2977 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2978 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2979 VikViewport *vvp = vik_window_viewport(vw);
2981 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2985 #ifdef VIK_CONFIG_GEOCACHES
2987 * Acquire into this TRW Layer from Geocaching.com
2989 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2991 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2992 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2993 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2994 VikViewport *vvp = vik_window_viewport(vw);
2996 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
3000 #ifdef VIK_CONFIG_GEOTAG
3002 * Acquire into this TRW Layer from images
3004 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3006 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3007 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3008 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3009 VikViewport *vvp = vik_window_viewport(vw);
3011 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3012 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
3014 // Reverify thumbnails as they may have changed
3015 vtl->has_verified_thumbnails = FALSE;
3016 trw_layer_verify_thumbnails ( vtl, NULL );
3020 static void trw_layer_gps_upload ( gpointer lav[2] )
3022 gpointer pass_along[6];
3023 pass_along[0] = lav[0];
3024 pass_along[1] = lav[1];
3025 pass_along[2] = NULL; // No track - operate on the layer
3026 pass_along[3] = NULL;
3027 pass_along[4] = NULL;
3028 pass_along[5] = NULL;
3030 trw_layer_gps_upload_any ( pass_along );
3034 * If pass_along[3] is defined that this will upload just that track
3036 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3038 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3039 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3041 // May not actually get a track here as pass_along[2&3] can be null
3042 VikTrack *track = NULL;
3043 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3044 gboolean xfer_all = FALSE;
3046 if ( pass_along[2] ) {
3048 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3049 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3052 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3053 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3056 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3059 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3063 else if ( !pass_along[4] )
3064 xfer_all = TRUE; // i.e. whole layer
3066 if (track && !track->visible) {
3067 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3071 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3072 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3073 GTK_DIALOG_DESTROY_WITH_PARENT,
3075 GTK_RESPONSE_ACCEPT,
3077 GTK_RESPONSE_REJECT,
3080 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3081 GtkWidget *response_w = NULL;
3082 #if GTK_CHECK_VERSION (2, 20, 0)
3083 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3087 gtk_widget_grab_focus ( response_w );
3089 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3091 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3092 datasource_gps_clean_up ( dgs );
3093 gtk_widget_destroy ( dialog );
3097 // Get info from reused datasource dialog widgets
3098 gchar* protocol = datasource_gps_get_protocol ( dgs );
3099 gchar* port = datasource_gps_get_descriptor ( dgs );
3100 // NB don't free the above strings as they're references to values held elsewhere
3101 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3102 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3103 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3104 gboolean turn_off = datasource_gps_get_off ( dgs );
3106 gtk_widget_destroy ( dialog );
3108 // When called from the viewport - work the corresponding layerspanel:
3110 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3113 // Apply settings to transfer to the GPS device
3120 vik_layers_panel_get_viewport (vlp),
3129 * Acquire into this TRW Layer from any GPS Babel supported file
3131 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3133 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3134 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3135 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3136 VikViewport *vvp = vik_window_viewport(vw);
3138 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3141 static void trw_layer_new_wp ( gpointer lav[2] )
3143 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3144 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3145 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3146 instead return true if you want to update. */
3147 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 )
3148 vik_layers_panel_emit_update ( vlp );
3151 static void trw_layer_new_track ( gpointer lav[2] )
3153 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3155 if ( ! vtl->current_track ) {
3156 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3157 vtl->current_track = vik_track_new();
3158 vtl->current_track->visible = TRUE;
3159 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3161 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3165 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3167 vtl->current_track = vik_track_new();
3168 vtl->current_track->visible = TRUE;
3169 vtl->current_track->is_route = TRUE;
3170 // By default make all routes red
3171 vtl->current_track->has_color = TRUE;
3172 gdk_color_parse ( "red", &vtl->current_track->color );
3173 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3176 static void trw_layer_new_route ( gpointer lav[2] )
3178 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3180 if ( ! vtl->current_track ) {
3181 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3182 new_route_create_common ( vtl, name );
3183 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3187 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3189 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3190 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3192 if ( g_hash_table_size (vtl->routes) > 0 ) {
3193 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3194 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3195 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3196 vik_layers_panel_emit_update ( vlp );
3201 static void trw_layer_finish_track ( gpointer lav[2] )
3203 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3204 vtl->current_track = NULL;
3205 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3208 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3210 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3211 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3213 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3214 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3215 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3216 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3217 vik_layers_panel_emit_update ( vlp );
3221 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
3223 /* NB do not care if wp is visible or not */
3224 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3227 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3229 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3230 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3232 /* Only 1 waypoint - jump straight to it */
3233 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3234 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3235 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3237 /* If at least 2 waypoints - find center and then zoom to fit */
3238 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3240 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3241 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3242 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3245 vik_layers_panel_emit_update ( vlp );
3248 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3250 static gpointer pass_along[2];
3252 GtkWidget *export_submenu;
3253 pass_along[0] = vtl;
3254 pass_along[1] = vlp;
3256 item = gtk_menu_item_new();
3257 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3258 gtk_widget_show ( item );
3260 if ( vtl->current_track ) {
3261 if ( vtl->current_track->is_route )
3262 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3264 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3267 gtk_widget_show ( item );
3270 item = gtk_menu_item_new ();
3271 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3272 gtk_widget_show ( item );
3275 /* Now with icons */
3276 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3277 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3279 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3280 gtk_widget_show ( item );
3282 GtkWidget *view_submenu = gtk_menu_new();
3283 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3284 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3285 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3286 gtk_widget_show ( item );
3287 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3289 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3291 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3292 gtk_widget_show ( item );
3294 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3296 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3297 gtk_widget_show ( item );
3299 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3300 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3301 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3302 gtk_widget_show ( item );
3304 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3305 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3306 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3307 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3308 gtk_widget_show ( item );
3310 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3311 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3312 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3313 gtk_widget_show ( item );
3315 export_submenu = gtk_menu_new ();
3316 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3317 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3318 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3319 gtk_widget_show ( item );
3320 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3322 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3323 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3324 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3325 gtk_widget_show ( item );
3327 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3328 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3329 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3330 gtk_widget_show ( item );
3332 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3333 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3334 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3335 gtk_widget_show ( item );
3337 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3338 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3339 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3340 gtk_widget_show ( item );
3342 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3343 item = gtk_menu_item_new_with_mnemonic ( external1 );
3344 g_free ( external1 );
3345 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3346 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3347 gtk_widget_show ( item );
3349 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3350 item = gtk_menu_item_new_with_mnemonic ( external2 );
3351 g_free ( external2 );
3352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3353 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3354 gtk_widget_show ( item );
3356 GtkWidget *new_submenu = gtk_menu_new();
3357 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3358 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3359 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3360 gtk_widget_show(item);
3361 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3363 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3364 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3365 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3366 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3367 gtk_widget_show ( item );
3369 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3370 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3372 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3373 gtk_widget_show ( item );
3374 // Make it available only when a new track *not* already in progress
3375 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3377 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3378 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3380 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3381 gtk_widget_show ( item );
3382 // Make it available only when a new track *not* already in progress
3383 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3385 #ifdef VIK_CONFIG_GEONAMES
3386 GtkWidget *wikipedia_submenu = gtk_menu_new();
3387 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3388 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3389 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3390 gtk_widget_show(item);
3391 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3393 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3394 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3395 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3396 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3397 gtk_widget_show ( item );
3399 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3400 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3401 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3402 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3403 gtk_widget_show ( item );
3406 #ifdef VIK_CONFIG_GEOTAG
3407 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3408 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3409 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3410 gtk_widget_show ( item );
3413 GtkWidget *acquire_submenu = gtk_menu_new ();
3414 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3415 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3416 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3417 gtk_widget_show ( item );
3418 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3420 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3421 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3422 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3423 gtk_widget_show ( item );
3425 #ifdef VIK_CONFIG_GOOGLE
3426 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3427 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3428 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3429 gtk_widget_show ( item );
3432 #ifdef VIK_CONFIG_OPENSTREETMAP
3433 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3434 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3435 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3436 gtk_widget_show ( item );
3439 #ifdef VIK_CONFIG_GEOCACHES
3440 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3442 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3443 gtk_widget_show ( item );
3446 #ifdef VIK_CONFIG_GEOTAG
3447 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3449 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3450 gtk_widget_show ( item );
3453 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3455 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3456 gtk_widget_show ( item );
3458 GtkWidget *upload_submenu = gtk_menu_new ();
3459 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3460 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3461 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3462 gtk_widget_show ( item );
3463 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3465 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3466 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3468 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3469 gtk_widget_show ( item );
3471 #ifdef VIK_CONFIG_OPENSTREETMAP
3472 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3473 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3474 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3475 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3476 gtk_widget_show ( item );
3479 GtkWidget *delete_submenu = gtk_menu_new ();
3480 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3481 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3482 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3483 gtk_widget_show ( item );
3484 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3486 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3489 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3490 gtk_widget_show ( item );
3492 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3493 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3495 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3496 gtk_widget_show ( item );
3498 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3499 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3500 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3501 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3502 gtk_widget_show ( item );
3504 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3507 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3508 gtk_widget_show ( item );
3510 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3511 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3513 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3514 gtk_widget_show ( item );
3517 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3518 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3520 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3521 gtk_widget_show ( item );
3525 // Fake Waypoint UUIDs vi simple increasing integer
3526 static guint wp_uuid = 0;
3528 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3532 vik_waypoint_set_name (wp, name);
3534 if ( VIK_LAYER(vtl)->realized )
3536 // Do we need to create the sublayer:
3537 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3538 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3541 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3543 // Visibility column always needed for waypoints
3544 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3545 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
3547 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
3549 // Actual setting of visibility dependent on the waypoint
3550 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3552 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3555 highest_wp_number_add_wp(vtl, name);
3556 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3560 // Fake Track UUIDs vi simple increasing integer
3561 static guint tr_uuid = 0;
3563 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3567 vik_track_set_name (t, name);
3569 if ( VIK_LAYER(vtl)->realized )
3571 // Do we need to create the sublayer:
3572 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3573 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3576 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3577 // Visibility column always needed for tracks
3578 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3579 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
3581 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
3583 // Actual setting of visibility dependent on the track
3584 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3586 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3589 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3591 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3594 // Fake Route UUIDs vi simple increasing integer
3595 static guint rt_uuid = 0;
3597 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3601 vik_track_set_name (t, name);
3603 if ( VIK_LAYER(vtl)->realized )
3605 // Do we need to create the sublayer:
3606 if ( g_hash_table_size (vtl->routes) == 0 ) {
3607 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3610 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3611 // Visibility column always needed for tracks
3612 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3613 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3615 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3617 // Actual setting of visibility dependent on the track
3618 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3620 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3623 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3625 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3628 /* to be called whenever a track has been deleted or may have been changed. */
3629 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3631 if (vtl->current_tp_track == trk )
3632 trw_layer_cancel_current_tp ( vtl, FALSE );
3635 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3638 gchar *newname = g_strdup(name);
3643 switch ( sublayer_type ) {
3644 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3645 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3647 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3648 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3651 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3654 // If found a name already in use try adding 1 to it and we try again
3656 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3658 newname = new_newname;
3661 } while ( id != NULL);
3666 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3668 // No more uniqueness of name forced when loading from a file
3669 // This now makes this function a little redunant as we just flow the parameters through
3670 vik_trw_layer_add_waypoint ( vtl, name, wp );
3673 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3675 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3676 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3677 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3678 vik_track_free ( tr );
3679 vtl->route_finder_append = FALSE; /* this means we have added it */
3682 // No more uniqueness of name forced when loading from a file
3684 vik_trw_layer_add_route ( vtl, name, tr );
3686 vik_trw_layer_add_track ( vtl, name, tr );
3688 if ( vtl->route_finder_check_added_track ) {
3689 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3690 vtl->route_finder_added_track = tr;
3695 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3697 *l = g_list_append(*l, id);
3701 * Move an item from one TRW layer to another TRW layer
3703 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3705 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3706 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3708 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3710 VikTrack *trk2 = vik_track_copy ( trk );
3711 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3712 vik_trw_layer_delete_track ( vtl_src, trk );
3715 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3716 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3718 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3720 VikTrack *trk2 = vik_track_copy ( trk );
3721 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3722 vik_trw_layer_delete_route ( vtl_src, trk );
3725 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3726 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3728 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3730 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3731 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3732 trw_layer_delete_waypoint ( vtl_src, wp );
3736 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3738 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3739 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3741 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3742 GList *items = NULL;
3745 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3746 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3748 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3749 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3751 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3752 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3757 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3758 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3760 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3761 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3763 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3770 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3771 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3776 VikTrack *trk; // input
3777 gpointer uuid; // output
3780 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3782 trku_udata *user_data = udata;
3783 if ( trk == user_data->trk ) {
3784 user_data->uuid = id;
3790 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3792 gboolean was_visible = FALSE;
3794 if ( trk && trk->name ) {
3796 if ( trk == vtl->current_track ) {
3797 vtl->current_track = NULL;
3798 vtl->current_tp_track = NULL;
3799 vtl->current_tp_id = NULL;
3800 vtl->moving_tp = FALSE;
3803 was_visible = trk->visible;
3805 if ( trk == vtl->route_finder_current_track )
3806 vtl->route_finder_current_track = NULL;
3808 if ( trk == vtl->route_finder_added_track )
3809 vtl->route_finder_added_track = NULL;
3815 // Hmmm, want key of it
3816 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3818 if ( trkf && udata.uuid ) {
3819 /* could be current_tp, so we have to check */
3820 trw_layer_cancel_tps_of_track ( vtl, trk );
3822 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3825 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3826 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3827 g_hash_table_remove ( vtl->tracks, udata.uuid );
3829 // If last sublayer, then remove sublayer container
3830 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3831 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3839 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
3841 gboolean was_visible = FALSE;
3843 if ( trk && trk->name ) {
3845 if ( trk == vtl->current_track ) {
3846 vtl->current_track = NULL;
3847 vtl->current_tp_track = NULL;
3848 vtl->current_tp_id = NULL;
3849 vtl->moving_tp = FALSE;
3852 was_visible = trk->visible;
3854 if ( trk == vtl->route_finder_current_track )
3855 vtl->route_finder_current_track = NULL;
3857 if ( trk == vtl->route_finder_added_track )
3858 vtl->route_finder_added_track = NULL;
3864 // Hmmm, want key of it
3865 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
3867 if ( trkf && udata.uuid ) {
3868 /* could be current_tp, so we have to check */
3869 trw_layer_cancel_tps_of_track ( vtl, trk );
3871 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
3874 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3875 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
3876 g_hash_table_remove ( vtl->routes, udata.uuid );
3878 // If last sublayer, then remove sublayer container
3879 if ( g_hash_table_size (vtl->routes) == 0 ) {
3880 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
3888 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3890 gboolean was_visible = FALSE;
3892 if ( wp && wp->name ) {
3894 if ( wp == vtl->current_wp ) {
3895 vtl->current_wp = NULL;
3896 vtl->current_wp_id = NULL;
3897 vtl->moving_wp = FALSE;
3900 was_visible = wp->visible;
3906 // Hmmm, want key of it
3907 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3909 if ( wpf && udata.uuid ) {
3910 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3913 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3914 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3916 highest_wp_number_remove_wp(vtl, wp->name);
3917 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3919 // If last sublayer, then remove sublayer container
3920 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3921 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3931 // Only for temporary use by trw_layer_delete_waypoint_by_name
3932 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3934 wpu_udata *user_data = udata;
3935 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3936 user_data->uuid = id;
3943 * Delete a waypoint by the given name
3944 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3945 * as there be multiple waypoints with the same name
3947 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3950 // Fake a waypoint with the given name
3951 udata.wp = vik_waypoint_new ();
3952 vik_waypoint_set_name (udata.wp, name);
3953 // Currently only the name is used in this waypoint find function
3956 // Hmmm, want key of it
3957 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3959 vik_waypoint_free (udata.wp);
3961 if ( wpf && udata.uuid )
3962 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3968 VikTrack *trk; // input
3969 gpointer uuid; // output
3972 // Only for temporary use by trw_layer_delete_track_by_name
3973 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3975 tpu_udata *user_data = udata;
3976 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3977 user_data->uuid = id;
3984 * Delete a track by the given name
3985 * NOTE: ATM this will delete the first encountered Track with the specified name
3986 * as there may be multiple tracks with the same name within the specified hash table
3988 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
3991 // Fake a track with the given name
3992 udata.trk = vik_track_new ();
3993 vik_track_set_name (udata.trk, name);
3994 // Currently only the name is used in this waypoint find function
3997 // Hmmm, want key of it
3998 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4000 vik_track_free (udata.trk);
4002 if ( trkf && udata.uuid ) {
4003 // This could be a little better written...
4004 if ( vtl->tracks == ht_tracks )
4005 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4006 if ( vtl->routes == ht_tracks )
4007 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4014 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
4016 vik_treeview_item_delete (vt, it );
4019 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4022 vtl->current_track = NULL;
4023 vtl->route_finder_current_track = NULL;
4024 vtl->route_finder_added_track = NULL;
4025 if (vtl->current_tp_track)
4026 trw_layer_cancel_current_tp(vtl, FALSE);
4028 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4029 g_hash_table_remove_all(vtl->routes_iters);
4030 g_hash_table_remove_all(vtl->routes);
4032 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4034 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4037 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4040 vtl->current_track = NULL;
4041 vtl->route_finder_current_track = NULL;
4042 vtl->route_finder_added_track = NULL;
4043 if (vtl->current_tp_track)
4044 trw_layer_cancel_current_tp(vtl, FALSE);
4046 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4047 g_hash_table_remove_all(vtl->tracks_iters);
4048 g_hash_table_remove_all(vtl->tracks);
4050 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4052 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4055 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4057 vtl->current_wp = NULL;
4058 vtl->current_wp_id = NULL;
4059 vtl->moving_wp = FALSE;
4061 highest_wp_number_reset(vtl);
4063 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4064 g_hash_table_remove_all(vtl->waypoints_iters);
4065 g_hash_table_remove_all(vtl->waypoints);
4067 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4069 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4072 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4074 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4075 // Get confirmation from the user
4076 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4077 _("Are you sure you want to delete all tracks in %s?"),
4078 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4079 vik_trw_layer_delete_all_tracks (vtl);
4082 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4084 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4085 // Get confirmation from the user
4086 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4087 _("Are you sure you want to delete all routes in %s?"),
4088 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4089 vik_trw_layer_delete_all_routes (vtl);
4092 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4094 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4095 // Get confirmation from the user
4096 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4097 _("Are you sure you want to delete all waypoints in %s?"),
4098 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4099 vik_trw_layer_delete_all_waypoints (vtl);
4102 static void trw_layer_delete_item ( gpointer pass_along[6] )
4104 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4105 gboolean was_visible = FALSE;
4106 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4108 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4109 if ( wp && wp->name ) {
4110 if ( GPOINTER_TO_INT ( pass_along[4]) )
4111 // Get confirmation from the user
4112 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4113 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4114 _("Are you sure you want to delete the waypoint \"%s\""),
4117 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4120 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4122 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4123 if ( trk && trk->name ) {
4124 if ( GPOINTER_TO_INT ( pass_along[4]) )
4125 // Get confirmation from the user
4126 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4127 _("Are you sure you want to delete the track \"%s\""),
4130 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4135 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4136 if ( trk && trk->name ) {
4137 if ( GPOINTER_TO_INT ( pass_along[4]) )
4138 // Get confirmation from the user
4139 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4140 _("Are you sure you want to delete the route \"%s\""),
4143 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4147 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4151 static void trw_layer_properties_item ( gpointer pass_along[7] )
4153 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4154 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4156 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4158 if ( wp && wp->name )
4160 gboolean updated = FALSE;
4161 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
4163 if ( updated && pass_along[6] )
4164 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4166 if ( updated && VIK_LAYER(vtl)->visible )
4167 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4173 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4174 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4176 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4178 if ( tr && tr->name )
4180 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4183 pass_along[1], /* vlp */
4184 pass_along[5], /* vvp */
4185 pass_along[6]); /* iter */
4191 * Update the treeview of the track id - primarily to update the icon
4193 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4199 gpointer *trkf = NULL;
4200 if ( trk->is_route )
4201 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4203 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4205 if ( trkf && udata.uuid ) {
4207 GtkTreeIter *iter = NULL;
4208 if ( trk->is_route )
4209 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4211 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4214 // TODO: Make this a function
4215 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4216 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4217 ((trk->color.green & 0xff00) << 8) |
4218 (trk->color.blue & 0xff00);
4219 gdk_pixbuf_fill ( pixbuf, pixel );
4220 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4221 g_object_unref (pixbuf);
4228 Parameter 1 -> VikLayersPanel
4229 Parameter 2 -> VikLayer
4230 Parameter 3 -> VikViewport
4232 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4235 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4236 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4239 /* since vlp not set, vl & vvp should be valid instead! */
4241 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4242 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
4247 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4249 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4251 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4252 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4254 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4256 if ( track && track->trackpoints )
4257 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4260 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4262 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4264 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4265 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4267 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4269 if ( track && track->trackpoints )
4271 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4273 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4274 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4275 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4276 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4277 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4281 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4283 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4285 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4286 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4288 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4293 // Converting a track to a route can be a bit more complicated,
4294 // so give a chance to change our minds:
4295 if ( !trk->is_route &&
4296 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4297 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4299 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4300 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4305 VikTrack *trk_copy = vik_track_copy ( trk );
4308 trk_copy->is_route = !trk_copy->is_route;
4310 // ATM can't set name to self - so must create temporary copy
4311 gchar *name = g_strdup ( trk_copy->name );
4313 // Delete old one and then add new one
4314 if ( trk->is_route ) {
4315 vik_trw_layer_delete_route ( vtl, trk );
4316 vik_trw_layer_add_track ( vtl, name, trk_copy );
4319 // Extra route conversion bits...
4320 vik_track_merge_segments ( trk_copy );
4321 vik_track_to_routepoints ( trk_copy );
4323 vik_trw_layer_delete_track ( vtl, trk );
4324 vik_trw_layer_add_route ( vtl, name, trk_copy );
4328 // Update in case color of track / route changes when moving between sublayers
4329 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4333 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4335 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4337 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4338 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4340 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4345 vtl->current_track = track;
4346 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, track->is_route ? TOOL_CREATE_ROUTE : TOOL_CREATE_TRACK);
4348 if ( track->trackpoints )
4349 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4352 #ifdef VIK_CONFIG_GOOGLE
4354 * extend a track using route finder
4356 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4358 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4359 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4360 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4362 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
4363 vtl->route_finder_coord = last_coord;
4364 vtl->route_finder_current_track = track;
4365 vtl->route_finder_started = TRUE;
4367 if ( track->trackpoints )
4368 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4373 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4375 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4376 /* Also warn if overwrite old elevation data */
4377 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4379 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4380 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4382 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4385 vik_track_apply_dem_data ( track );
4388 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4390 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4392 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4393 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4395 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4400 GList *trps = track->trackpoints;
4403 trps = g_list_last(trps);
4404 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4407 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4409 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4411 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4412 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4414 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4419 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4422 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4425 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4427 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4429 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4430 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4432 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4437 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4440 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4443 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4445 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4447 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4448 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4450 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4455 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4458 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4462 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4464 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4466 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4468 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4469 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4471 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4473 if ( trk && trk->trackpoints )
4475 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4476 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4477 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4478 if ( pass_along[1] )
4479 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4481 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4485 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4487 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4488 trw_layer_tpwin_init ( vtl );
4491 /*************************************
4492 * merge/split by time routines
4493 *************************************/
4495 /* called for each key in track hash table.
4496 * If the current track has the same time stamp type, add it to the result,
4497 * except the one pointed by "exclude".
4498 * set exclude to NULL if there is no exclude to check.
4499 * Note that the result is in reverse (for performance reasons).
4504 gboolean with_timestamps;
4506 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4508 twt_udata *user_data = udata;
4509 VikTrackpoint *p1, *p2;
4511 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4515 if (VIK_TRACK(value)->trackpoints) {
4516 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4517 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4519 if ( user_data->with_timestamps ) {
4520 if (!p1->has_timestamp || !p2->has_timestamp) {
4525 // Don't add tracks with timestamps when getting non timestamp tracks
4526 if (p1->has_timestamp || p2->has_timestamp) {
4532 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4535 /* called for each key in track hash table. if original track user_data[1] is close enough
4536 * to the passed one, add it to list in user_data[0]
4538 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4541 VikTrackpoint *p1, *p2;
4542 VikTrack *trk = VIK_TRACK(value);
4544 GList **nearby_tracks = ((gpointer *)user_data)[0];
4545 GList *tpoints = ((gpointer *)user_data)[1];
4548 * detect reasons for not merging, and return
4549 * if no reason is found not to merge, then do it.
4552 // Exclude the original track from the compiled list
4553 if (trk->trackpoints == tpoints) {
4557 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4558 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4560 if (trk->trackpoints) {
4561 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4562 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4564 if (!p1->has_timestamp || !p2->has_timestamp) {
4565 //g_print("no timestamp\n");
4569 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4570 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4571 if (! (abs(t1 - p2->timestamp) < threshold ||
4573 abs(p1->timestamp - t2) < threshold)
4580 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4583 /* comparison function used to sort tracks; a and b are hash table keys */
4584 /* Not actively used - can be restored if needed
4585 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4587 GHashTable *tracks = user_data;
4590 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4591 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4593 if (t1 < t2) return -1;
4594 if (t1 > t2) return 1;
4599 /* comparison function used to sort trackpoints */
4600 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4602 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4604 if (t1 < t2) return -1;
4605 if (t1 > t2) return 1;
4610 * comparison function which can be used to sort tracks or waypoints by name
4612 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4614 const gchar* namea = (const gchar*) a;
4615 const gchar* nameb = (const gchar*) b;
4616 if ( namea == NULL || nameb == NULL)
4619 // Same sort method as used in the vik_treeview_*_alphabetize functions
4620 return strcmp ( namea, nameb );
4624 * Attempt to merge selected track with other tracks specified by the user
4625 * Tracks to merge with must be of the same 'type' as the selected track -
4626 * either all with timestamps, or all without timestamps
4628 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4630 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4631 GList *other_tracks = NULL;
4632 GHashTable *ght_tracks;
4633 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4634 ght_tracks = vtl->routes;
4636 ght_tracks = vtl->tracks;
4638 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4643 if ( !track->trackpoints )
4647 udata.result = &other_tracks;
4648 udata.exclude = track->trackpoints;
4649 // Allow merging with 'similar' time type time tracks
4650 // i.e. either those times, or those without
4651 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4653 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4654 other_tracks = g_list_reverse(other_tracks);
4656 if ( !other_tracks ) {
4657 if ( udata.with_timestamps )
4658 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4660 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4664 // Sort alphabetically for user presentation
4665 // Convert into list of names for usage with dialog function
4666 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4667 GList *other_tracks_names = NULL;
4668 GList *iter = g_list_first ( other_tracks );
4670 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4671 iter = g_list_next ( iter );
4674 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4676 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4680 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4681 g_list_free(other_tracks);
4682 g_list_free(other_tracks_names);
4687 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4688 VikTrack *merge_track;
4689 if ( track->is_route )
4690 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4692 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4695 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4696 merge_track->trackpoints = NULL;
4697 if ( track->is_route )
4698 vik_trw_layer_delete_route (vtl, merge_track);
4700 vik_trw_layer_delete_track (vtl, merge_track);
4701 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4704 /* TODO: free data before free merge_list */
4705 for (l = merge_list; l != NULL; l = g_list_next(l))
4707 g_list_free(merge_list);
4708 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4712 // c.f. trw_layer_sorted_track_id_by_name_list
4713 // but don't add the specified track to the list (normally current track)
4714 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4716 twt_udata *user_data = udata;
4719 if (trk->trackpoints == user_data->exclude) {
4723 // Sort named list alphabetically
4724 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4728 * Join - this allows combining 'tracks' and 'track routes'
4729 * i.e. doesn't care about whether tracks have consistent timestamps
4730 * ATM can only append one track at a time to the currently selected track
4732 static void trw_layer_append_track ( gpointer pass_along[6] )
4735 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4737 GHashTable *ght_tracks;
4738 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4739 ght_tracks = vtl->routes;
4741 ght_tracks = vtl->tracks;
4743 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4748 GList *other_tracks_names = NULL;
4750 // Sort alphabetically for user presentation
4751 // Convert into list of names for usage with dialog function
4752 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4754 udata.result = &other_tracks_names;
4755 udata.exclude = trk->trackpoints;
4757 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4759 // Note the limit to selecting one track only
4760 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4761 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4762 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4765 trk->is_route ? _("Append Route"): _("Append Track"),
4766 trk->is_route ? _("Select the route to append after the current route") :
4767 _("Select the track to append after the current track") );
4769 g_list_free(other_tracks_names);
4771 // It's a list, but shouldn't contain more than one other track!
4772 if ( append_list ) {
4774 for (l = append_list; l != NULL; l = g_list_next(l)) {
4775 // TODO: at present this uses the first track found by name,
4776 // which with potential multiple same named tracks may not be the one selected...
4777 VikTrack *append_track;
4778 if ( trk->is_route )
4779 append_track = vik_trw_layer_get_route ( vtl, l->data );
4781 append_track = vik_trw_layer_get_track ( vtl, l->data );
4783 if ( append_track ) {
4784 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4785 append_track->trackpoints = NULL;
4786 if ( trk->is_route )
4787 vik_trw_layer_delete_route (vtl, append_track);
4789 vik_trw_layer_delete_track (vtl, append_track);
4792 for (l = append_list; l != NULL; l = g_list_next(l))
4794 g_list_free(append_list);
4795 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4800 * Very similar to trw_layer_append_track for joining
4801 * but this allows selection from the 'other' list
4802 * If a track is selected, then is shows routes and joins the selected one
4803 * If a route is selected, then is shows tracks and joins the selected one
4805 static void trw_layer_append_other ( gpointer pass_along[6] )
4808 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4810 GHashTable *ght_mykind, *ght_others;
4811 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4812 ght_mykind = vtl->routes;
4813 ght_others = vtl->tracks;
4816 ght_mykind = vtl->tracks;
4817 ght_others = vtl->routes;
4820 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
4825 GList *other_tracks_names = NULL;
4827 // Sort alphabetically for user presentation
4828 // Convert into list of names for usage with dialog function
4829 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4831 udata.result = &other_tracks_names;
4832 udata.exclude = trk->trackpoints;
4834 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4836 // Note the limit to selecting one track only
4837 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4838 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4839 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4842 trk->is_route ? _("Append Track"): _("Append Route"),
4843 trk->is_route ? _("Select the track to append after the current route") :
4844 _("Select the route to append after the current track") );
4846 g_list_free(other_tracks_names);
4848 // It's a list, but shouldn't contain more than one other track!
4849 if ( append_list ) {
4851 for (l = append_list; l != NULL; l = g_list_next(l)) {
4852 // TODO: at present this uses the first track found by name,
4853 // which with potential multiple same named tracks may not be the one selected...
4855 // Get FROM THE OTHER TYPE list
4856 VikTrack *append_track;
4857 if ( trk->is_route )
4858 append_track = vik_trw_layer_get_track ( vtl, l->data );
4860 append_track = vik_trw_layer_get_route ( vtl, l->data );
4862 if ( append_track ) {
4864 if ( !append_track->is_route &&
4865 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
4866 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
4868 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4869 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
4870 vik_track_merge_segments ( append_track );
4871 vik_track_to_routepoints ( append_track );
4878 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4879 append_track->trackpoints = NULL;
4881 // Delete copied which is FROM THE OTHER TYPE list
4882 if ( trk->is_route )
4883 vik_trw_layer_delete_track (vtl, append_track);
4885 vik_trw_layer_delete_route (vtl, append_track);
4888 for (l = append_list; l != NULL; l = g_list_next(l))
4890 g_list_free(append_list);
4891 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4895 /* merge by segments */
4896 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4898 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4899 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4900 guint segments = vik_track_merge_segments ( trk );
4901 // NB currently no need to redraw as segments not actually shown on the display
4902 // However inform the user of what happened:
4904 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4905 g_snprintf(str, 64, tmp_str, segments);
4906 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4909 /* merge by time routine */
4910 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4912 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4916 GList *tracks_with_timestamp = NULL;
4917 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4918 if (orig_trk->trackpoints &&
4919 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4920 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4925 udata.result = &tracks_with_timestamp;
4926 udata.exclude = orig_trk->trackpoints;
4927 udata.with_timestamps = TRUE;
4928 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4929 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4931 if (!tracks_with_timestamp) {
4932 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4935 g_list_free(tracks_with_timestamp);
4937 static guint threshold_in_minutes = 1;
4938 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4939 _("Merge Threshold..."),
4940 _("Merge when time between tracks less than:"),
4941 &threshold_in_minutes)) {
4945 // keep attempting to merge all tracks until no merges within the time specified is possible
4946 gboolean attempt_merge = TRUE;
4947 GList *nearby_tracks = NULL;
4949 static gpointer params[3];
4951 while ( attempt_merge ) {
4953 // Don't try again unless tracks have changed
4954 attempt_merge = FALSE;
4956 trps = orig_trk->trackpoints;
4960 if (nearby_tracks) {
4961 g_list_free(nearby_tracks);
4962 nearby_tracks = NULL;
4965 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4966 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4968 /* g_print("Original track times: %d and %d\n", t1, t2); */
4969 params[0] = &nearby_tracks;
4970 params[1] = (gpointer)trps;
4971 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4973 /* get a list of adjacent-in-time tracks */
4974 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4977 GList *l = nearby_tracks;
4980 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4981 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4983 t1 = get_first_trackpoint(l)->timestamp;
4984 t2 = get_last_trackpoint(l)->timestamp;
4985 #undef get_first_trackpoint
4986 #undef get_last_trackpoint
4987 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4990 /* remove trackpoints from merged track, delete track */
4991 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4992 VIK_TRACK(l->data)->trackpoints = NULL;
4993 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4995 // Tracks have changed, therefore retry again against all the remaining tracks
4996 attempt_merge = TRUE;
5001 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5004 g_list_free(nearby_tracks);
5005 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5009 * Split a track at the currently selected trackpoint
5011 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5013 if ( !vtl->current_tpl )
5016 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5017 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5019 VikTrack *tr = vik_track_new ();
5020 GList *newglist = g_list_alloc ();
5021 newglist->prev = NULL;
5022 newglist->next = vtl->current_tpl->next;
5023 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5024 tr->trackpoints = newglist;
5025 tr->is_route = vtl->current_tp_track->is_route;
5026 tr->color = vtl->current_tp_track->color;
5029 vtl->current_tpl->next->prev = newglist; /* end old track here */
5030 vtl->current_tpl->next = NULL;
5032 vtl->current_tpl = newglist; /* change tp to first of new track. */
5033 vtl->current_tp_track = tr;
5036 vik_trw_layer_add_route ( vtl, name, tr );
5038 vik_trw_layer_add_track ( vtl, name, tr );
5044 // Also need id of newly created track
5047 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5049 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5051 if ( trkf && udata.uuid )
5052 vtl->current_tp_id = udata.uuid;
5054 vtl->current_tp_id = NULL;
5056 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5061 /* split by time routine */
5062 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5064 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5065 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5066 GList *trps = track->trackpoints;
5068 GList *newlists = NULL;
5069 GList *newtps = NULL;
5070 static guint thr = 1;
5077 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5078 _("Split Threshold..."),
5079 _("Split when time between trackpoints exceeds:"),
5084 /* iterate through trackpoints, and copy them into new lists without touching original list */
5085 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5089 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5091 g_print("panic: ts < prev_ts: this should never happen!\n");
5094 if (ts - prev_ts > thr*60) {
5095 /* flush accumulated trackpoints into new list */
5096 newlists = g_list_append(newlists, g_list_reverse(newtps));
5100 /* accumulate trackpoint copies in newtps, in reverse order */
5101 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5103 iter = g_list_next(iter);
5106 newlists = g_list_append(newlists, g_list_reverse(newtps));
5109 /* put lists of trackpoints into tracks */
5111 // Only bother updating if the split results in new tracks
5112 if (g_list_length (newlists) > 1) {
5117 tr = vik_track_new();
5118 tr->visible = track->visible;
5119 tr->trackpoints = (GList *)(iter->data);
5121 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5122 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5123 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5124 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5126 iter = g_list_next(iter);
5128 // Remove original track and then update the display
5129 vik_trw_layer_delete_track (vtl, track);
5130 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
5132 g_list_free(newlists);
5136 * Split a track by the number of points as specified by the user
5138 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5140 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5142 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5143 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5145 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5150 // Check valid track
5151 GList *trps = track->trackpoints;
5155 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5156 _("Split Every Nth Point"),
5157 _("Split on every Nth point:"),
5158 250, // Default value as per typical limited track capacity of various GPS devices
5162 // Was a valid number returned?
5168 GList *newlists = NULL;
5169 GList *newtps = NULL;
5174 /* accumulate trackpoint copies in newtps, in reverse order */
5175 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5177 if (count >= points) {
5178 /* flush accumulated trackpoints into new list */
5179 newlists = g_list_append(newlists, g_list_reverse(newtps));
5183 iter = g_list_next(iter);
5186 // If there is a remaining chunk put that into the new split list
5187 // This may well be the whole track if no split points were encountered
5189 newlists = g_list_append(newlists, g_list_reverse(newtps));
5192 /* put lists of trackpoints into tracks */
5194 // Only bother updating if the split results in new tracks
5195 if (g_list_length (newlists) > 1) {
5200 tr = vik_track_new();
5201 tr->visible = track->visible;
5202 tr->is_route = track->is_route;
5203 tr->color = track->color;
5204 tr->trackpoints = (GList *)(iter->data);
5206 if ( track->is_route ) {
5207 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5208 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5211 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5212 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5214 iter = g_list_next(iter);
5216 // Remove original track and then update the display
5217 if ( track->is_route )
5218 vik_trw_layer_delete_route (vtl, track);
5220 vik_trw_layer_delete_track (vtl, track);
5221 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
5223 g_list_free(newlists);
5227 * Split a track at the currently selected trackpoint
5229 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5231 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5232 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5233 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5237 * Split a track by its segments
5238 * Routes do not have segments so don't call this for routes
5240 static void trw_layer_split_segments ( gpointer pass_along[6] )
5242 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5243 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5250 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5253 for ( i = 0; i < ntracks; i++ ) {
5255 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5256 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5261 // Remove original track
5262 vik_trw_layer_delete_track ( vtl, trk );
5263 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5266 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5269 /* end of split/merge routines */
5272 * Delete adjacent track points at the same position
5273 * AKA Delete Dulplicates on the Properties Window
5275 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5277 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5279 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5280 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5282 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5287 gulong removed = vik_track_remove_dup_points ( trk );
5289 // Track has been updated so update tps:
5290 trw_layer_cancel_tps_of_track ( vtl, trk );
5292 // Inform user how much was deleted as it's not obvious from the normal view
5294 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5295 g_snprintf(str, 64, tmp_str, removed);
5296 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5298 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5302 * Delete adjacent track points with the same timestamp
5303 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5305 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5307 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5309 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5310 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5312 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5317 gulong removed = vik_track_remove_same_time_points ( trk );
5319 // Track has been updated so update tps:
5320 trw_layer_cancel_tps_of_track ( vtl, trk );
5322 // Inform user how much was deleted as it's not obvious from the normal view
5324 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5325 g_snprintf(str, 64, tmp_str, removed);
5326 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5328 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5334 static void trw_layer_reverse ( gpointer pass_along[6] )
5336 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5338 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5339 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5341 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5346 // Check valid track
5347 GList *trps = track->trackpoints;
5351 vik_track_reverse ( track );
5353 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
5357 * Similar to trw_layer_enum_item, but this uses a sorted method
5360 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5362 GList **list = (GList**)udata;
5363 // *list = g_list_prepend(*all, key); //unsorted method
5364 // Sort named list alphabetically
5365 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5370 * Now Waypoint specific sort
5372 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5374 GList **list = (GList**)udata;
5375 // Sort named list alphabetically
5376 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5380 * Track specific sort
5382 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5384 GList **list = (GList**)udata;
5385 // Sort named list alphabetically
5386 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5391 gboolean has_same_track_name;
5392 const gchar *same_track_name;
5393 } same_track_name_udata;
5395 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5397 const gchar* namea = (const gchar*) aa;
5398 const gchar* nameb = (const gchar*) bb;
5401 gint result = strcmp ( namea, nameb );
5403 if ( result == 0 ) {
5404 // Found two names the same
5405 same_track_name_udata *user_data = udata;
5406 user_data->has_same_track_name = TRUE;
5407 user_data->same_track_name = namea;
5410 // Leave ordering the same
5415 * Find out if any tracks have the same name in this hash table
5417 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5419 // Sort items by name, then compare if any next to each other are the same
5421 GList *track_names = NULL;
5422 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5425 if ( ! track_names )
5428 same_track_name_udata udata;
5429 udata.has_same_track_name = FALSE;
5431 // Use sort routine to traverse list comparing items
5432 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5433 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5434 // Still no tracks...
5438 return udata.has_same_track_name;
5442 * Force unqiue track names for the track table specified
5443 * Note the panel is a required parameter to enable the update of the names displayed
5444 * Specify if on tracks or else on routes
5446 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5448 // . Search list for an instance of repeated name
5449 // . get track of this name
5450 // . create new name
5451 // . rename track & update equiv. treeview iter
5452 // . repeat until all different
5454 same_track_name_udata udata;
5456 GList *track_names = NULL;
5457 udata.has_same_track_name = FALSE;
5458 udata.same_track_name = NULL;
5460 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5463 if ( ! track_names )
5466 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5468 // Still no tracks...
5469 if ( ! dummy_list1 )
5472 while ( udata.has_same_track_name ) {
5474 // Find a track with the same name
5477 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5479 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5483 g_critical("Houston, we've had a problem.");
5484 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5485 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5490 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5491 vik_track_set_name ( trk, newname );
5497 // Need want key of it for treeview update
5498 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5500 if ( trkf && udataU.uuid ) {
5504 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5506 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5509 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5510 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5511 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5516 // Start trying to find same names again...
5518 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5519 udata.has_same_track_name = FALSE;
5520 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5522 // No tracks any more - give up searching
5523 if ( ! dummy_list2 )
5524 udata.has_same_track_name = FALSE;
5528 vik_layers_panel_emit_update ( vlp );
5534 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5536 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5539 // Ensure list of track names offered is unique
5540 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5541 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5542 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5543 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5549 // Sort list alphabetically for better presentation
5550 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5553 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5557 // Get list of items to delete from the user
5558 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5561 _("Delete Selection"),
5562 _("Select tracks to delete"));
5565 // Delete requested tracks
5566 // since specificly requested, IMHO no need for extra confirmation
5567 if ( delete_list ) {
5569 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5570 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5571 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5573 g_list_free(delete_list);
5574 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5581 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5583 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5586 // Ensure list of track names offered is unique
5587 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5588 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5589 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5590 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5596 // Sort list alphabetically for better presentation
5597 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5600 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5604 // Get list of items to delete from the user
5605 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5608 _("Delete Selection"),
5609 _("Select routes to delete") );
5612 // Delete requested routes
5613 // since specificly requested, IMHO no need for extra confirmation
5614 if ( delete_list ) {
5616 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5617 // This deletes first route it finds of that name (but uniqueness is enforced above)
5618 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5620 g_list_free(delete_list);
5621 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5626 gboolean has_same_waypoint_name;
5627 const gchar *same_waypoint_name;
5628 } same_waypoint_name_udata;
5630 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5632 const gchar* namea = (const gchar*) aa;
5633 const gchar* nameb = (const gchar*) bb;
5636 gint result = strcmp ( namea, nameb );
5638 if ( result == 0 ) {
5639 // Found two names the same
5640 same_waypoint_name_udata *user_data = udata;
5641 user_data->has_same_waypoint_name = TRUE;
5642 user_data->same_waypoint_name = namea;
5645 // Leave ordering the same
5650 * Find out if any waypoints have the same name in this layer
5652 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5654 // Sort items by name, then compare if any next to each other are the same
5656 GList *waypoint_names = NULL;
5657 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5660 if ( ! waypoint_names )
5663 same_waypoint_name_udata udata;
5664 udata.has_same_waypoint_name = FALSE;
5666 // Use sort routine to traverse list comparing items
5667 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5668 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5669 // Still no waypoints...
5673 return udata.has_same_waypoint_name;
5677 * Force unqiue waypoint names for this layer
5678 * Note the panel is a required parameter to enable the update of the names displayed
5680 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5682 // . Search list for an instance of repeated name
5683 // . get waypoint of this name
5684 // . create new name
5685 // . rename waypoint & update equiv. treeview iter
5686 // . repeat until all different
5688 same_waypoint_name_udata udata;
5690 GList *waypoint_names = NULL;
5691 udata.has_same_waypoint_name = FALSE;
5692 udata.same_waypoint_name = NULL;
5694 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5697 if ( ! waypoint_names )
5700 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5702 // Still no waypoints...
5703 if ( ! dummy_list1 )
5706 while ( udata.has_same_waypoint_name ) {
5708 // Find a waypoint with the same name
5709 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5713 g_critical("Houston, we've had a problem.");
5714 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5715 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5720 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5721 vik_waypoint_set_name ( waypoint, newname );
5724 udataU.wp = waypoint;
5727 // Need want key of it for treeview update
5728 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5730 if ( wpf && udataU.uuid ) {
5732 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5735 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5736 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5737 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5742 // Start trying to find same names again...
5743 waypoint_names = NULL;
5744 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5745 udata.has_same_waypoint_name = FALSE;
5746 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5748 // No waypoints any more - give up searching
5749 if ( ! dummy_list2 )
5750 udata.has_same_waypoint_name = FALSE;
5754 vik_layers_panel_emit_update ( vlp );
5760 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5762 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5765 // Ensure list of waypoint names offered is unique
5766 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5767 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5768 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5769 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5775 // Sort list alphabetically for better presentation
5776 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5778 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5782 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5784 // Get list of items to delete from the user
5785 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5788 _("Delete Selection"),
5789 _("Select waypoints to delete"));
5792 // Delete requested waypoints
5793 // since specificly requested, IMHO no need for extra confirmation
5794 if ( delete_list ) {
5796 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5797 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5798 trw_layer_delete_waypoint_by_name (vtl, l->data);
5800 g_list_free(delete_list);
5801 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5806 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5808 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5810 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5813 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5815 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
5816 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5820 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
5822 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5824 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
5826 // No actual change to the name supplied
5827 if (strcmp(newname, wp->name) == 0 )
5830 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5833 // An existing waypoint has been found with the requested name
5834 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5835 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
5840 // Update WP name and refresh the treeview
5841 vik_waypoint_set_name (wp, newname);
5843 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5844 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5847 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5852 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5854 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
5856 // No actual change to the name supplied
5857 if (strcmp(newname, trk->name) == 0)
5860 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
5863 // An existing track has been found with the requested name
5864 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5865 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
5869 // Update track name and refresh GUI parts
5870 vik_track_set_name (trk, newname);
5872 // Update any subwindows that could be displaying this track which has changed name
5873 // Only one Track Edit Window
5874 if ( l->current_tp_track == trk && l->tpwin ) {
5875 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5877 // Property Dialog of the track
5878 vik_trw_layer_propwin_update ( trk );
5880 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5881 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5884 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5889 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5891 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
5893 // No actual change to the name supplied
5894 if (strcmp(newname, trk->name) == 0)
5897 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
5900 // An existing track has been found with the requested name
5901 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5902 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
5906 // Update track name and refresh GUI parts
5907 vik_track_set_name (trk, newname);
5909 // Update any subwindows that could be displaying this track which has changed name
5910 // Only one Track Edit Window
5911 if ( l->current_tp_track == trk && l->tpwin ) {
5912 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5914 // Property Dialog of the track
5915 vik_trw_layer_propwin_update ( trk );
5917 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5918 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5921 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5928 static gboolean is_valid_geocache_name ( gchar *str )
5930 gint len = strlen ( str );
5931 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]));
5934 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
5936 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5937 a_acquire_set_filter_track ( trk );
5940 #ifdef VIK_CONFIG_GOOGLE
5941 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
5943 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
5944 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
5947 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
5949 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5951 gchar *escaped = uri_escape ( tr->comment );
5952 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
5953 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5960 /* vlp can be NULL if necessary - i.e. right-click from a tool */
5961 /* viewpoint is now available instead */
5962 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
5964 static gpointer pass_along[8];
5966 gboolean rv = FALSE;
5969 pass_along[1] = vlp;
5970 pass_along[2] = GINT_TO_POINTER (subtype);
5971 pass_along[3] = sublayer;
5972 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5973 pass_along[5] = vvp;
5974 pass_along[6] = iter;
5975 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
5977 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5981 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5982 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5983 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5984 gtk_widget_show ( item );
5986 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5987 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
5988 if (tr && tr->property_dialog)
5989 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5991 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
5992 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
5993 if (tr && tr->property_dialog)
5994 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5997 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5999 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6000 gtk_widget_show ( item );
6002 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6004 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6005 gtk_widget_show ( item );
6007 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6009 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6010 gtk_widget_show ( item );
6012 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6014 gboolean separator_created = FALSE;
6016 /* could be a right-click using the tool */
6017 if ( vlp != NULL ) {
6018 item = gtk_menu_item_new ();
6019 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6020 gtk_widget_show ( item );
6022 separator_created = TRUE;
6024 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6025 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6026 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6027 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6028 gtk_widget_show ( item );
6031 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6033 if ( wp && wp->name ) {
6034 if ( is_valid_geocache_name ( wp->name ) ) {
6036 if ( !separator_created ) {
6037 item = gtk_menu_item_new ();
6038 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6039 gtk_widget_show ( item );
6040 separator_created = TRUE;
6043 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6044 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6045 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6046 gtk_widget_show ( item );
6050 if ( wp && wp->image )
6052 if ( !separator_created ) {
6053 item = gtk_menu_item_new ();
6054 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6055 gtk_widget_show ( item );
6056 separator_created = TRUE;
6059 // Set up image paramater
6060 pass_along[5] = wp->image;
6062 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6063 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
6064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6065 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6066 gtk_widget_show ( item );
6068 #ifdef VIK_CONFIG_GEOTAG
6069 GtkWidget *geotag_submenu = gtk_menu_new ();
6070 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6071 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6072 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6073 gtk_widget_show ( item );
6074 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6076 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6078 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6079 gtk_widget_show ( item );
6081 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6083 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6084 gtk_widget_show ( item );
6091 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6092 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6094 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6095 gtk_widget_show ( item );
6096 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6097 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6098 gtk_widget_set_sensitive ( item, TRUE );
6100 gtk_widget_set_sensitive ( item, FALSE );
6103 item = gtk_menu_item_new ();
6104 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6105 gtk_widget_show ( item );
6108 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6111 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6112 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6114 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6115 gtk_widget_show ( item );
6118 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6120 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6121 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6122 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6123 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6124 gtk_widget_show ( item );
6126 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6127 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6128 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6129 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6130 gtk_widget_show ( item );
6132 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6133 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6134 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6135 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6136 gtk_widget_show ( item );
6138 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6141 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6142 gtk_widget_show ( item );
6145 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6149 if ( l->current_track && !l->current_track->is_route ) {
6150 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6151 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6152 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6153 gtk_widget_show ( item );
6155 item = gtk_menu_item_new ();
6156 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6157 gtk_widget_show ( item );
6160 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6161 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6163 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6164 gtk_widget_show ( item );
6166 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6167 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6169 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6170 gtk_widget_show ( item );
6171 // Make it available only when a new track *not* already in progress
6172 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6174 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6177 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6178 gtk_widget_show ( item );
6180 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6183 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6184 gtk_widget_show ( item );
6187 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6191 if ( l->current_track && l->current_track->is_route ) {
6192 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6193 // Reuse finish track method
6194 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6195 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6196 gtk_widget_show ( item );
6198 item = gtk_menu_item_new ();
6199 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6200 gtk_widget_show ( item );
6203 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6204 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6205 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6206 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6207 gtk_widget_show ( item );
6209 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6210 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6211 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6212 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6213 gtk_widget_show ( item );
6214 // Make it available only when a new track *not* already in progress
6215 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6217 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6218 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6219 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6220 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6221 gtk_widget_show ( item );
6223 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6224 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6225 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6226 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6227 gtk_widget_show ( item );
6230 GtkWidget *upload_submenu = gtk_menu_new ();
6232 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6234 item = gtk_menu_item_new ();
6235 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6236 gtk_widget_show ( item );
6238 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6239 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6240 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6241 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6242 if ( l->current_track ) {
6243 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6244 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6245 gtk_widget_show ( item );
6248 item = gtk_menu_item_new ();
6249 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6250 gtk_widget_show ( item );
6253 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6254 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6256 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6257 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6259 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6260 gtk_widget_show ( item );
6262 GtkWidget *goto_submenu;
6263 goto_submenu = gtk_menu_new ();
6264 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6265 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6267 gtk_widget_show ( item );
6268 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6270 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6271 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6272 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6273 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6274 gtk_widget_show ( item );
6276 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6277 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6279 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6280 gtk_widget_show ( item );
6282 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6283 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6285 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6286 gtk_widget_show ( item );
6288 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6289 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6291 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6292 gtk_widget_show ( item );
6294 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6295 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6297 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6298 gtk_widget_show ( item );
6300 // Routes don't have speeds
6301 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6302 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6303 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6305 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6306 gtk_widget_show ( item );
6309 GtkWidget *combine_submenu;
6310 combine_submenu = gtk_menu_new ();
6311 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6312 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6313 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6314 gtk_widget_show ( item );
6315 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6317 // Routes don't have times or segments...
6318 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6319 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6320 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6321 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6322 gtk_widget_show ( item );
6324 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6325 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6326 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6327 gtk_widget_show ( item );
6330 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6331 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6332 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6333 gtk_widget_show ( item );
6335 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6336 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6338 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6340 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6341 gtk_widget_show ( item );
6343 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6344 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6346 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6347 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6348 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6349 gtk_widget_show ( item );
6351 GtkWidget *split_submenu;
6352 split_submenu = gtk_menu_new ();
6353 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6354 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6355 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6356 gtk_widget_show ( item );
6357 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6359 // Routes don't have times or segments...
6360 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6361 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6363 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6364 gtk_widget_show ( item );
6366 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6367 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6369 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6370 gtk_widget_show ( item );
6373 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6374 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6375 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6376 gtk_widget_show ( item );
6378 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6380 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6381 gtk_widget_show ( item );
6382 // Make it available only when a trackpoint is selected.
6383 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6385 GtkWidget *delete_submenu;
6386 delete_submenu = gtk_menu_new ();
6387 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6388 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6389 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6390 gtk_widget_show ( item );
6391 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6393 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6395 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6396 gtk_widget_show ( item );
6398 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6399 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6400 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6401 gtk_widget_show ( item );
6403 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6404 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6406 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6407 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6408 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6409 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6410 gtk_widget_show ( item );
6412 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6414 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6415 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6417 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6418 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
6419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6420 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6421 gtk_widget_show ( item );
6424 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6425 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
6426 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6427 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6428 gtk_widget_show ( item );
6430 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6431 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6433 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6434 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6435 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6436 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6437 gtk_widget_show ( item );
6439 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6440 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6442 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6443 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6444 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6445 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6446 gtk_widget_show ( item );
6448 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6449 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6451 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6452 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6453 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6454 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6455 gtk_widget_show ( item );
6457 #ifdef VIK_CONFIG_GOOGLE
6458 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6459 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
6460 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6461 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6462 gtk_widget_show ( item );
6465 // ATM can't upload a single waypoint but can do waypoints to a GPS
6466 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6467 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6468 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6469 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6470 gtk_widget_show ( item );
6471 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6473 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6474 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6475 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6476 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6477 gtk_widget_show ( item );
6481 // Some things aren't usable with routes
6482 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6483 #ifdef VIK_CONFIG_OPENSTREETMAP
6484 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6485 // Convert internal pointer into actual track for usage outside this file
6486 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6489 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6490 gtk_widget_show ( item );
6493 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6494 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6495 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6496 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6497 gtk_widget_show ( item );
6499 #ifdef VIK_CONFIG_GOOGLE
6500 if ( is_valid_google_route ( l, sublayer ) )
6502 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6503 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
6505 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6506 gtk_widget_show ( item );
6510 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6512 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6513 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6514 gtk_widget_show ( item );
6516 /* ATM This function is only available via the layers panel, due to needing a vlp */
6518 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6519 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6520 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6522 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6523 gtk_widget_show ( item );
6527 #ifdef VIK_CONFIG_GEOTAG
6528 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6530 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6531 gtk_widget_show ( item );
6535 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6536 // Only show on viewport popmenu when a trackpoint is selected
6537 if ( ! vlp && l->current_tpl ) {
6539 item = gtk_menu_item_new ();
6540 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6541 gtk_widget_show ( item );
6543 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6544 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6546 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6547 gtk_widget_show ( item );
6554 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6557 if (!vtl->current_tpl)
6559 if (!vtl->current_tpl->next)
6562 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6563 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6565 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6568 VikTrackpoint *tp_new = vik_trackpoint_new();
6569 struct LatLon ll_current, ll_next;
6570 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6571 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6573 /* main positional interpolation */
6574 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6575 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6577 /* Now other properties that can be interpolated */
6578 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6580 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6581 /* Note here the division is applied to each part, then added
6582 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6583 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6584 tp_new->has_timestamp = TRUE;
6587 if (tp_current->speed != NAN && tp_next->speed != NAN)
6588 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6590 /* TODO - improve interpolation of course, as it may not be correct.
6591 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6592 [similar applies if value is in radians] */
6593 if (tp_current->course != NAN && tp_next->course != NAN)
6594 tp_new->speed = (tp_current->course + tp_next->course)/2;
6596 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6598 /* Insert new point into the trackpoints list after the current TP */
6599 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6601 // Otherwise try routes
6602 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6606 gint index = g_list_index ( trk->trackpoints, tp_current );
6608 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6613 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6619 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6623 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6625 if ( vtl->current_tpl )
6627 vtl->current_tpl = NULL;
6628 vtl->current_tp_track = NULL;
6629 vtl->current_tp_id = NULL;
6630 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6634 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6636 g_assert ( vtl->tpwin != NULL );
6637 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6638 trw_layer_cancel_current_tp ( vtl, TRUE );
6640 if ( vtl->current_tpl == NULL )
6643 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6645 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6646 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6648 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6650 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6652 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6658 // Find available adjacent trackpoint
6659 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6661 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6662 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6664 // Delete current trackpoint
6665 vik_trackpoint_free ( vtl->current_tpl->data );
6666 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6668 // Set to current to the available adjacent trackpoint
6669 vtl->current_tpl = new_tpl;
6671 // Reset dialog with the available adjacent trackpoint
6672 if ( vtl->current_tp_track )
6673 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6675 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6679 // Delete current trackpoint
6680 vik_trackpoint_free ( vtl->current_tpl->data );
6681 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6682 trw_layer_cancel_current_tp ( vtl, FALSE );
6685 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6687 if ( vtl->current_tp_track )
6688 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6689 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
6691 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6693 if ( vtl->current_tp_track )
6694 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6695 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6697 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6699 trw_layer_insert_tp_after_current_tp ( vtl );
6700 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6702 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6703 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6706 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
6710 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6711 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
6712 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
6713 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
6714 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
6716 if ( vtl->current_tpl )
6717 if ( vtl->current_tp_track )
6718 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6719 /* set layer name and TP data */
6722 /***************************************************************************
6724 ***************************************************************************/
6726 /*** Utility data structures and functions ****/
6730 gint closest_x, closest_y;
6731 gpointer *closest_wp_id;
6732 VikWaypoint *closest_wp;
6738 gint closest_x, closest_y;
6739 gpointer closest_track_id;
6740 VikTrackpoint *closest_tp;
6745 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
6751 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
6753 // If waypoint has an image then use the image size to select
6755 gint slackx, slacky;
6756 slackx = wp->image_width / 2;
6757 slacky = wp->image_height / 2;
6759 if ( x <= params->x + slackx && x >= params->x - slackx
6760 && y <= params->y + slacky && y >= params->y - slacky ) {
6761 params->closest_wp_id = id;
6762 params->closest_wp = wp;
6763 params->closest_x = x;
6764 params->closest_y = y;
6767 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
6768 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
6769 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6771 params->closest_wp_id = id;
6772 params->closest_wp = wp;
6773 params->closest_x = x;
6774 params->closest_y = y;
6778 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
6780 GList *tpl = t->trackpoints;
6789 tp = VIK_TRACKPOINT(tpl->data);
6791 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
6793 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
6794 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
6795 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6797 params->closest_track_id = id;
6798 params->closest_tp = tp;
6799 params->closest_tpl = tpl;
6800 params->closest_x = x;
6801 params->closest_y = y;
6807 // ATM: Leave this as 'Track' only.
6808 // Not overly bothered about having a snap to route trackpoint capability
6809 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6811 TPSearchParams params;
6815 params.closest_track_id = NULL;
6816 params.closest_tp = NULL;
6817 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
6818 return params.closest_tp;
6821 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6823 WPSearchParams params;
6827 params.closest_wp = NULL;
6828 params.closest_wp_id = NULL;
6829 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
6830 return params.closest_wp;
6834 // Some forward declarations
6835 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
6836 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
6837 static void marker_end_move ( tool_ed_t *t );
6840 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6844 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6846 // Here always allow snapping back to the original location
6847 // this is useful when one decides not to move the thing afterall
6848 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
6851 if ( event->state & GDK_CONTROL_MASK )
6853 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6855 new_coord = tp->coord;
6859 if ( event->state & GDK_SHIFT_MASK )
6861 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6863 new_coord = wp->coord;
6867 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6869 marker_moveto ( t, x, y );
6876 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6878 if ( t->holding && event->button == 1 )
6881 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6884 if ( event->state & GDK_CONTROL_MASK )
6886 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6888 new_coord = tp->coord;
6892 if ( event->state & GDK_SHIFT_MASK )
6894 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6896 new_coord = wp->coord;
6899 marker_end_move ( t );
6901 // Determine if working on a waypoint or a trackpoint
6902 if ( t->is_waypoint )
6903 vtl->current_wp->coord = new_coord;
6905 if ( vtl->current_tpl ) {
6906 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6909 if ( vtl->current_tp_track )
6910 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6915 vtl->current_wp = NULL;
6916 vtl->current_wp_id = NULL;
6917 trw_layer_cancel_current_tp ( vtl, FALSE );
6919 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6926 Returns true if a waypoint or track is found near the requested event position for this particular layer
6927 The item found is automatically selected
6928 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
6930 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
6932 if ( event->button != 1 )
6935 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6938 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
6941 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
6943 if (vtl->waypoints_visible) {
6944 WPSearchParams wp_params;
6945 wp_params.vvp = vvp;
6946 wp_params.x = event->x;
6947 wp_params.y = event->y;
6948 wp_params.closest_wp_id = NULL;
6949 wp_params.closest_wp = NULL;
6951 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
6953 if ( wp_params.closest_wp ) {
6956 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
6958 // Too easy to move it so must be holding shift to start immediately moving it
6959 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
6960 if ( event->state & GDK_SHIFT_MASK ||
6961 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
6962 // Put into 'move buffer'
6963 // NB vvp & vw already set in tet
6964 tet->vtl = (gpointer)vtl;
6965 tet->is_waypoint = TRUE;
6967 marker_begin_move (tet, event->x, event->y);
6970 vtl->current_wp = wp_params.closest_wp;
6971 vtl->current_wp_id = wp_params.closest_wp_id;
6973 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6979 // Used for both track and route lists
6980 TPSearchParams tp_params;
6981 tp_params.vvp = vvp;
6982 tp_params.x = event->x;
6983 tp_params.y = event->y;
6984 tp_params.closest_track_id = NULL;
6985 tp_params.closest_tp = NULL;
6987 if (vtl->tracks_visible) {
6988 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
6990 if ( tp_params.closest_tp ) {
6992 // Always select + highlight the track
6993 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
6995 tet->is_waypoint = FALSE;
6997 // Select the Trackpoint
6998 // Can move it immediately when control held or it's the previously selected tp
6999 if ( event->state & GDK_CONTROL_MASK ||
7000 vtl->current_tpl == tp_params.closest_tpl ) {
7001 // Put into 'move buffer'
7002 // NB vvp & vw already set in tet
7003 tet->vtl = (gpointer)vtl;
7004 marker_begin_move (tet, event->x, event->y);
7007 vtl->current_tpl = tp_params.closest_tpl;
7008 vtl->current_tp_id = tp_params.closest_track_id;
7009 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7011 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7014 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7016 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7021 // Try again for routes
7022 if (vtl->routes_visible) {
7023 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7025 if ( tp_params.closest_tp ) {
7027 // Always select + highlight the track
7028 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7030 tet->is_waypoint = FALSE;
7032 // Select the Trackpoint
7033 // Can move it immediately when control held or it's the previously selected tp
7034 if ( event->state & GDK_CONTROL_MASK ||
7035 vtl->current_tpl == tp_params.closest_tpl ) {
7036 // Put into 'move buffer'
7037 // NB vvp & vw already set in tet
7038 tet->vtl = (gpointer)vtl;
7039 marker_begin_move (tet, event->x, event->y);
7042 vtl->current_tpl = tp_params.closest_tpl;
7043 vtl->current_tp_id = tp_params.closest_track_id;
7044 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7046 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7049 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7051 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7056 /* these aren't the droids you're looking for */
7057 vtl->current_wp = NULL;
7058 vtl->current_wp_id = NULL;
7059 trw_layer_cancel_current_tp ( vtl, FALSE );
7062 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7067 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7069 if ( event->button != 3 )
7072 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7075 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7078 /* Post menu for the currently selected item */
7080 /* See if a track is selected */
7081 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7082 if ( track && track->visible ) {
7084 if ( track->name ) {
7086 if ( vtl->track_right_click_menu )
7087 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
7089 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7096 if ( track->is_route )
7097 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7099 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7101 if ( trkf && udataU.uuid ) {
7104 if ( track->is_route )
7105 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7107 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7109 trw_layer_sublayer_add_menu_items ( vtl,
7110 vtl->track_right_click_menu,
7112 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7118 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7124 /* See if a waypoint is selected */
7125 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7126 if ( waypoint && waypoint->visible ) {
7127 if ( waypoint->name ) {
7129 if ( vtl->wp_right_click_menu )
7130 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
7132 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7135 udata.wp = waypoint;
7138 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7140 if ( wpf && udata.uuid ) {
7141 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7143 trw_layer_sublayer_add_menu_items ( vtl,
7144 vtl->wp_right_click_menu,
7146 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7151 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7160 /* background drawing hook, to be passed the viewport */
7161 static gboolean tool_sync_done = TRUE;
7163 static gboolean tool_sync(gpointer data)
7165 VikViewport *vvp = data;
7166 gdk_threads_enter();
7167 vik_viewport_sync(vvp);
7168 tool_sync_done = TRUE;
7169 gdk_threads_leave();
7173 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7176 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7177 gdk_gc_set_function ( t->gc, GDK_INVERT );
7178 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7179 vik_viewport_sync(t->vvp);
7184 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7186 VikViewport *vvp = t->vvp;
7187 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7188 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7192 if (tool_sync_done) {
7193 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7194 tool_sync_done = FALSE;
7198 static void marker_end_move ( tool_ed_t *t )
7200 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7201 g_object_unref ( t->gc );
7205 /*** Edit waypoint ****/
7207 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7209 tool_ed_t *t = g_new(tool_ed_t, 1);
7215 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7217 WPSearchParams params;
7218 tool_ed_t *t = data;
7219 VikViewport *vvp = t->vvp;
7221 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7228 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7231 if ( vtl->current_wp && vtl->current_wp->visible )
7233 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7235 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7237 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7238 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7240 if ( event->button == 3 )
7241 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7243 marker_begin_move(t, event->x, event->y);
7250 params.x = event->x;
7251 params.y = event->y;
7252 params.closest_wp_id = NULL;
7253 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7254 params.closest_wp = NULL;
7255 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7256 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7258 // how do we get here?
7259 marker_begin_move(t, event->x, event->y);
7260 g_critical("shouldn't be here");
7263 else if ( params.closest_wp )
7265 if ( event->button == 3 )
7266 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7268 vtl->waypoint_rightclick = FALSE;
7270 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7272 vtl->current_wp = params.closest_wp;
7273 vtl->current_wp_id = params.closest_wp_id;
7275 /* could make it so don't update if old WP is off screen and new is null but oh well */
7276 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7280 vtl->current_wp = NULL;
7281 vtl->current_wp_id = NULL;
7282 vtl->waypoint_rightclick = FALSE;
7283 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7287 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7289 tool_ed_t *t = data;
7290 VikViewport *vvp = t->vvp;
7292 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7297 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7300 if ( event->state & GDK_CONTROL_MASK )
7302 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7304 new_coord = tp->coord;
7308 if ( event->state & GDK_SHIFT_MASK )
7310 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7311 if ( wp && wp != vtl->current_wp )
7312 new_coord = wp->coord;
7317 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7319 marker_moveto ( t, x, y );
7326 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7328 tool_ed_t *t = data;
7329 VikViewport *vvp = t->vvp;
7331 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7334 if ( t->holding && event->button == 1 )
7337 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7340 if ( event->state & GDK_CONTROL_MASK )
7342 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7344 new_coord = tp->coord;
7348 if ( event->state & GDK_SHIFT_MASK )
7350 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7351 if ( wp && wp != vtl->current_wp )
7352 new_coord = wp->coord;
7355 marker_end_move ( t );
7357 vtl->current_wp->coord = new_coord;
7358 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7361 /* PUT IN RIGHT PLACE!!! */
7362 if ( event->button == 3 && vtl->waypoint_rightclick )
7364 if ( vtl->wp_right_click_menu )
7365 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7366 if ( vtl->current_wp ) {
7367 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7368 trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_id, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_id ), vvp );
7369 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7371 vtl->waypoint_rightclick = FALSE;
7376 /*** New track ****/
7378 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7385 GdkDrawable *drawable;
7391 * Draw specified pixmap
7393 static gboolean draw_sync ( gpointer data )
7395 draw_sync_t *ds = (draw_sync_t*) data;
7396 // Sometimes don't want to draw
7397 // normally because another update has taken precedent such as panning the display
7398 // which means this pixmap is no longer valid
7399 if ( ds->vtl->draw_sync_do ) {
7400 gdk_threads_enter();
7401 gdk_draw_drawable (ds->drawable,
7404 0, 0, 0, 0, -1, -1);
7405 ds->vtl->draw_sync_done = TRUE;
7406 gdk_threads_leave();
7411 static gchar* distance_string (gdouble distance)
7415 /* draw label with distance */
7416 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7417 switch (dist_units) {
7418 case VIK_UNITS_DISTANCE_MILES:
7419 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7420 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7421 } else if (distance < 1609.4) {
7422 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7424 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7428 // VIK_UNITS_DISTANCE_KILOMETRES
7429 if (distance >= 1000 && distance < 100000) {
7430 g_sprintf(str, "%3.2f km", distance/1000.0);
7431 } else if (distance < 1000) {
7432 g_sprintf(str, "%d m", (int)distance);
7434 g_sprintf(str, "%d km", (int)distance/1000);
7438 return g_strdup (str);
7442 * Actually set the message in statusbar
7444 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
7446 // Only show elevation data when track has some elevation properties
7447 gchar str_gain_loss[64];
7448 str_gain_loss[0] = '\0';
7450 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7451 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7452 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7454 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7457 // Write with full gain/loss information
7458 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
7459 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
7464 * Figure out what information should be set in the statusbar and then write it
7466 static void update_statusbar ( VikTrwLayer *vtl )
7468 // Get elevation data
7469 gdouble elev_gain, elev_loss;
7470 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7472 /* Find out actual distance of current track */
7473 gdouble distance = vik_track_get_length (vtl->current_track);
7474 gchar *str = distance_string (distance);
7476 statusbar_write (str, elev_gain, elev_loss, vtl);
7482 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7484 /* if we haven't sync'ed yet, we don't have time to do more. */
7485 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7486 GList *iter = g_list_last ( vtl->current_track->trackpoints );
7487 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7489 static GdkPixmap *pixmap = NULL;
7491 // Need to check in case window has been resized
7492 w1 = vik_viewport_get_width(vvp);
7493 h1 = vik_viewport_get_height(vvp);
7495 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7497 gdk_drawable_get_size (pixmap, &w2, &h2);
7498 if (w1 != w2 || h1 != h2) {
7499 g_object_unref ( G_OBJECT ( pixmap ) );
7500 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7503 // Reset to background
7504 gdk_draw_drawable (pixmap,
7505 vtl->current_track_newpoint_gc,
7506 vik_viewport_get_pixmap(vvp),
7507 0, 0, 0, 0, -1, -1);
7509 draw_sync_t *passalong;
7512 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7514 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7515 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7516 // thus when we come to reset to the background it would include what we have already drawn!!
7517 gdk_draw_line ( pixmap,
7518 vtl->current_track_newpoint_gc,
7519 x1, y1, event->x, event->y );
7520 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7522 /* Find out actual distance of current track */
7523 gdouble distance = vik_track_get_length (vtl->current_track);
7525 // Now add distance to where the pointer is //
7528 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7529 vik_coord_to_latlon ( &coord, &ll );
7530 distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
7532 // Get elevation data
7533 gdouble elev_gain, elev_loss;
7534 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7536 // Adjust elevation data (if available) for the current pointer position
7538 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7539 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7540 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7541 // Adjust elevation of last track point
7542 if ( elev_new > last_tpt->altitude )
7544 elev_gain += elev_new - last_tpt->altitude;
7547 elev_loss += last_tpt->altitude - elev_new;
7551 gchar *str = distance_string (distance);
7553 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7554 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
7556 pango_layout_set_text (pl, str, -1);
7558 pango_layout_get_pixel_size ( pl, &wd, &hd );
7561 // offset from cursor a bit depending on font size
7565 // Create a background block to make the text easier to read over the background map
7566 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7567 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7568 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7570 g_object_unref ( G_OBJECT ( pl ) );
7571 g_object_unref ( G_OBJECT ( background_block_gc ) );
7573 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7574 passalong->vtl = vtl;
7575 passalong->pixmap = pixmap;
7576 passalong->drawable = GTK_WIDGET(vvp)->window;
7577 passalong->gc = vtl->current_track_newpoint_gc;
7579 // Update statusbar with full gain/loss information
7580 statusbar_write (str, elev_gain, elev_loss, vtl);
7584 // draw pixmap when we have time to
7585 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7586 vtl->draw_sync_done = FALSE;
7587 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7589 return VIK_LAYER_TOOL_ACK;
7592 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7594 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7595 vtl->current_track = NULL;
7596 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7598 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7600 if ( vtl->current_track->trackpoints )
7602 GList *last = g_list_last(vtl->current_track->trackpoints);
7603 g_free ( last->data );
7604 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7607 update_statusbar ( vtl );
7609 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7616 * Common function to handle trackpoint button requests on either a route or a track
7617 * . enables adding a point via normal click
7618 * . enables removal of last point via right click
7619 * . finishing of the track or route via double clicking
7621 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7625 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7628 if ( event->button == 2 ) {
7629 // As the display is panning, the new track pixmap is now invalid so don't draw it
7630 // otherwise this drawing done results in flickering back to an old image
7631 vtl->draw_sync_do = FALSE;
7635 if ( event->button == 3 )
7637 if ( !vtl->current_track )
7640 if ( vtl->current_track->trackpoints )
7642 GList *last = g_list_last(vtl->current_track->trackpoints);
7643 g_free ( last->data );
7644 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7646 update_statusbar ( vtl );
7648 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7652 if ( event->type == GDK_2BUTTON_PRESS )
7654 /* subtract last (duplicate from double click) tp then end */
7655 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7657 GList *last = g_list_last(vtl->current_track->trackpoints);
7658 g_free ( last->data );
7659 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7660 /* undo last, then end */
7661 vtl->current_track = NULL;
7663 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7667 tp = vik_trackpoint_new();
7668 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
7670 /* snap to other TP */
7671 if ( event->state & GDK_CONTROL_MASK )
7673 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7675 tp->coord = other_tp->coord;
7678 tp->newsegment = FALSE;
7679 tp->has_timestamp = FALSE;
7682 if ( vtl->current_track ) {
7683 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
7684 /* Auto attempt to get elevation from DEM data (if it's available) */
7685 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
7688 vtl->ct_x1 = vtl->ct_x2;
7689 vtl->ct_y1 = vtl->ct_y2;
7690 vtl->ct_x2 = event->x;
7691 vtl->ct_y2 = event->y;
7693 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7697 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7699 // ----------------------------------------------------- if current is a route - switch to new track
7700 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
7702 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
7703 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
7705 vtl->current_track = vik_track_new();
7706 vtl->current_track->visible = TRUE;
7707 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
7712 return tool_new_track_or_route_click ( vtl, event, vvp );
7715 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7717 if ( event->button == 2 ) {
7718 // Pan moving ended - enable potential point drawing again
7719 vtl->draw_sync_do = TRUE;
7720 vtl->draw_sync_done = TRUE;
7724 /*** New route ****/
7726 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
7731 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7733 // -------------------------- if current is a track - switch to new route
7734 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
7736 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
7737 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
7738 new_route_create_common ( vtl, name );
7742 return tool_new_track_or_route_click ( vtl, event, vvp );
7745 /*** New waypoint ****/
7747 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7752 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7755 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7757 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
7758 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
7759 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7764 /*** Edit trackpoint ****/
7766 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
7768 tool_ed_t *t = g_new(tool_ed_t, 1);
7774 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7776 tool_ed_t *t = data;
7777 VikViewport *vvp = t->vvp;
7778 TPSearchParams params;
7779 /* OUTDATED DOCUMENTATION:
7780 find 5 pixel range on each side. then put these UTM, and a pointer
7781 to the winning track name (and maybe the winning track itself), and a
7782 pointer to the winning trackpoint, inside an array or struct. pass
7783 this along, do a foreach on the tracks which will do a foreach on the
7786 params.x = event->x;
7787 params.y = event->y;
7788 params.closest_track_id = NULL;
7789 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7790 params.closest_tp = NULL;
7792 if ( event->button != 1 )
7795 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7798 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
7801 if ( vtl->current_tpl )
7803 /* 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.) */
7804 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7805 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
7810 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
7812 if ( current_tr->visible &&
7813 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7814 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
7815 marker_begin_move ( t, event->x, event->y );
7821 if ( vtl->tracks_visible )
7822 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7824 if ( params.closest_tp )
7826 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
7827 vtl->current_tpl = params.closest_tpl;
7828 vtl->current_tp_id = params.closest_track_id;
7829 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
7830 trw_layer_tpwin_init ( vtl );
7831 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7832 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7836 if ( vtl->routes_visible )
7837 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
7839 if ( params.closest_tp )
7841 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
7842 vtl->current_tpl = params.closest_tpl;
7843 vtl->current_tp_id = params.closest_track_id;
7844 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
7845 trw_layer_tpwin_init ( vtl );
7846 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7847 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7851 /* these aren't the droids you're looking for */
7855 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7857 tool_ed_t *t = data;
7858 VikViewport *vvp = t->vvp;
7860 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7866 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7869 if ( event->state & GDK_CONTROL_MASK )
7871 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7872 if ( tp && tp != vtl->current_tpl->data )
7873 new_coord = tp->coord;
7875 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7878 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7879 marker_moveto ( t, x, y );
7887 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7889 tool_ed_t *t = data;
7890 VikViewport *vvp = t->vvp;
7892 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7894 if ( event->button != 1)
7899 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7902 if ( event->state & GDK_CONTROL_MASK )
7904 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7905 if ( tp && tp != vtl->current_tpl->data )
7906 new_coord = tp->coord;
7909 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7911 marker_end_move ( t );
7913 /* diff dist is diff from orig */
7915 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7917 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7924 #ifdef VIK_CONFIG_GOOGLE
7925 /*** Route Finder ***/
7926 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
7931 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7934 if ( !vtl ) return FALSE;
7935 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
7936 if ( event->button == 3 && vtl->route_finder_current_track ) {
7938 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
7940 vtl->route_finder_coord = *new_end;
7942 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7943 /* remove last ' to:...' */
7944 if ( vtl->route_finder_current_track->comment ) {
7945 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
7946 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
7947 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
7948 last_to - vtl->route_finder_current_track->comment - 1);
7949 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
7954 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
7955 struct LatLon start, end;
7956 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
7957 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
7960 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
7961 vik_coord_to_latlon ( &(tmp), &end );
7962 vtl->route_finder_coord = tmp; /* for continuations */
7964 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
7965 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
7966 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
7968 vtl->route_finder_check_added_track = TRUE;
7969 vtl->route_finder_started = FALSE;
7972 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
7973 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
7974 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
7975 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
7976 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
7977 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
7980 /* see if anything was done -- a track was added or appended to */
7981 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
7982 vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
7983 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
7984 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
7985 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
7986 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
7988 vtl->route_finder_added_track = NULL;
7989 vtl->route_finder_check_added_track = FALSE;
7990 vtl->route_finder_append = FALSE;
7992 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7994 vtl->route_finder_started = TRUE;
7995 vtl->route_finder_coord = tmp;
7996 vtl->route_finder_current_track = NULL;
8002 /*** Show picture ****/
8004 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8009 /* Params are: vvp, event, last match found or NULL */
8010 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
8012 if ( wp->image && wp->visible )
8014 gint x, y, slackx, slacky;
8015 GdkEventButton *event = (GdkEventButton *) params[1];
8017 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8018 slackx = wp->image_width / 2;
8019 slacky = wp->image_height / 2;
8020 if ( x <= event->x + slackx && x >= event->x - slackx
8021 && y <= event->y + slacky && y >= event->y - slacky )
8023 params[2] = wp->image; /* we've found a match. however continue searching
8024 * since we want to find the last match -- that
8025 * is, the match that was drawn last. */
8030 static void trw_layer_show_picture ( gpointer pass_along[6] )
8032 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8034 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8037 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8038 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8039 g_free ( quoted_file );
8040 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8042 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
8043 g_error_free ( err );
8046 #endif /* WINDOWS */
8049 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8051 gpointer params[3] = { vvp, event, NULL };
8052 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8054 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8057 static gpointer pass_along[6];
8058 pass_along[0] = vtl;
8059 pass_along[5] = params[2];
8060 trw_layer_show_picture ( pass_along );
8061 return TRUE; /* found a match */
8064 return FALSE; /* go through other layers, searching for a match */
8067 /***************************************************************************
8069 ***************************************************************************/
8075 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
8077 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8078 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8081 /* Structure for thumbnail creating data used in the background thread */
8083 VikTrwLayer *vtl; // Layer needed for redrawing
8084 GSList *pics; // Image list
8085 } thumbnail_create_thread_data;
8087 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8089 guint total = g_slist_length(tctd->pics), done = 0;
8090 while ( tctd->pics )
8092 a_thumbnails_create ( (gchar *) tctd->pics->data );
8093 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8095 return -1; /* Abort thread */
8097 tctd->pics = tctd->pics->next;
8100 // Redraw to show the thumbnails as they are now created
8101 if ( IS_VIK_LAYER(tctd->vtl) )
8102 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
8107 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8109 while ( tctd->pics )
8111 g_free ( tctd->pics->data );
8112 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8117 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8119 if ( ! vtl->has_verified_thumbnails )
8121 GSList *pics = NULL;
8122 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8125 gint len = g_slist_length ( pics );
8126 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8127 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8130 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8132 (vik_thr_func) create_thumbnails_thread,
8134 (vik_thr_free_func) thumbnail_create_thread_free,
8142 static const gchar* my_track_colors ( gint ii )
8144 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8156 // Fast and reliable way of returning a colour
8157 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8160 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8162 GHashTableIter iter;
8163 gpointer key, value;
8167 g_hash_table_iter_init ( &iter, vtl->tracks );
8169 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8171 // Tracks get a random spread of colours if not already assigned
8172 if ( ! VIK_TRACK(value)->has_color ) {
8173 gdk_color_parse ( my_track_colors (ii) , &(VIK_TRACK(value)->color) );
8174 VIK_TRACK(value)->has_color = TRUE;
8177 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8180 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8186 g_hash_table_iter_init ( &iter, vtl->routes );
8188 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8190 // Routes get an intermix of reds
8191 if ( ! VIK_TRACK(value)->has_color ) {
8193 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8195 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8196 VIK_TRACK(value)->has_color = TRUE;
8199 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8205 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vp )
8207 trw_layer_verify_thumbnails ( vtl, vp );
8208 trw_layer_track_alloc_colors ( vtl );
8211 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8213 return vtl->coord_mode;
8217 * Uniquify the whole layer
8218 * Also requires the layers panel as the names shown there need updating too
8219 * Returns whether the operation was successful or not
8221 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8224 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8225 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8226 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8232 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8234 vik_coord_convert ( &(wp->coord), *dest_mode );
8237 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
8239 vik_track_convert ( tr, *dest_mode );
8242 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8244 if ( vtl->coord_mode != dest_mode )
8246 vtl->coord_mode = dest_mode;
8247 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8248 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8252 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8254 vtl->menu_selection = selection;
8257 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8259 return (vtl->menu_selection);
8262 /* ----------- Downloading maps along tracks --------------- */
8264 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8266 /* TODO: calculating based on current size of viewport */
8267 const gdouble w_at_zoom_0_125 = 0.0013;
8268 const gdouble h_at_zoom_0_125 = 0.0011;
8269 gdouble zoom_factor = zoom_level/0.125;
8271 wh->lat = h_at_zoom_0_125 * zoom_factor;
8272 wh->lon = w_at_zoom_0_125 * zoom_factor;
8274 return 0; /* all OK */
8277 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8279 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8280 (dist->lat >= ABS(to->north_south - from->north_south)))
8283 VikCoord *coord = g_malloc(sizeof(VikCoord));
8284 coord->mode = VIK_COORD_LATLON;
8286 if (ABS(gradient) < 1) {
8287 if (from->east_west > to->east_west)
8288 coord->east_west = from->east_west - dist->lon;
8290 coord->east_west = from->east_west + dist->lon;
8291 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8293 if (from->north_south > to->north_south)
8294 coord->north_south = from->north_south - dist->lat;
8296 coord->north_south = from->north_south + dist->lat;
8297 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8303 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8305 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8306 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8308 VikCoord *next = from;
8310 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8312 list = g_list_prepend(list, next);
8318 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8320 typedef struct _Rect {
8325 #define GLRECT(iter) ((Rect *)((iter)->data))
8328 GList *rects_to_download = NULL;
8331 if (get_download_area_width(vvp, zoom_level, &wh))
8334 GList *iter = tr->trackpoints;
8338 gboolean new_map = TRUE;
8339 VikCoord *cur_coord, tl, br;
8342 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8344 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8345 rect = g_malloc(sizeof(Rect));
8348 rect->center = *cur_coord;
8349 rects_to_download = g_list_prepend(rects_to_download, rect);
8354 gboolean found = FALSE;
8355 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8356 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8367 GList *fillins = NULL;
8368 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8369 /* seems that ATM the function get_next_coord works only for LATLON */
8370 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8371 /* fill-ins for far apart points */
8372 GList *cur_rect, *next_rect;
8373 for (cur_rect = rects_to_download;
8374 (next_rect = cur_rect->next) != NULL;
8375 cur_rect = cur_rect->next) {
8376 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8377 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8378 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8382 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8385 GList *iter = fillins;
8387 cur_coord = (VikCoord *)(iter->data);
8388 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8389 rect = g_malloc(sizeof(Rect));
8392 rect->center = *cur_coord;
8393 rects_to_download = g_list_prepend(rects_to_download, rect);
8398 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8399 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8403 for (iter = fillins; iter; iter = iter->next)
8405 g_list_free(fillins);
8407 if (rects_to_download) {
8408 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8409 g_free(rect_iter->data);
8410 g_list_free(rects_to_download);
8414 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8417 gint selected_map, default_map;
8418 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8419 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8420 gint selected_zoom, default_zoom;
8424 VikTrwLayer *vtl = pass_along[0];
8425 VikLayersPanel *vlp = pass_along[1];
8427 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8428 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8430 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8434 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8436 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8437 int num_maps = g_list_length(vmls);
8440 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8444 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8445 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8447 gchar **np = map_names;
8448 VikMapsLayer **lp = map_layers;
8449 for (i = 0; i < num_maps; i++) {
8450 gboolean dup = FALSE;
8451 vml = (VikMapsLayer *)(vmls->data);
8452 for (j = 0; j < i; j++) { /* no duplicate allowed */
8453 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8460 *np++ = vik_maps_layer_get_map_label(vml);
8466 num_maps = lp - map_layers;
8468 for (default_map = 0; default_map < num_maps; default_map++) {
8469 /* TODO: check for parent layer's visibility */
8470 if (VIK_LAYER(map_layers[default_map])->visible)
8473 default_map = (default_map == num_maps) ? 0 : default_map;
8475 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8476 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8477 if (cur_zoom == zoom_vals[default_zoom])
8480 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8482 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8485 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8488 for (i = 0; i < num_maps; i++)
8489 g_free(map_names[i]);
8497 /**** lowest waypoint number calculation ***/
8498 static gint highest_wp_number_name_to_number(const gchar *name) {
8499 if ( strlen(name) == 3 ) {
8501 if ( n < 100 && name[0] != '0' )
8503 if ( n < 10 && name[0] != '0' )
8511 static void highest_wp_number_reset(VikTrwLayer *vtl)
8513 vtl->highest_wp_number = -1;
8516 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8518 /* if is bigger that top, add it */
8519 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8520 if ( new_wp_num > vtl->highest_wp_number )
8521 vtl->highest_wp_number = new_wp_num;
8524 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8526 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8527 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8528 if ( vtl->highest_wp_number == old_wp_num ) {
8530 vtl->highest_wp_number--;
8532 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8533 /* search down until we find something that *does* exist */
8535 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8536 vtl->highest_wp_number--;
8537 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8542 /* get lowest unused number */
8543 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8546 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8548 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8549 return g_strdup(buf);