2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #define WAYPOINT_FONT "Sans 8"
27 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
28 /* viktrwlayer.c -- 2200 lines can make a difference in the state of things */
35 #include "vikmapslayer.h"
36 #include "viktrwlayer_tpwin.h"
37 #include "viktrwlayer_propwin.h"
38 #include "garminsymbols.h"
39 #include "thumbnails.h"
40 #include "background.h"
45 #include "geonamessearch.h"
46 #ifdef VIK_CONFIG_OPENSTREETMAP
47 #include "osm-traces.h"
52 #include "icons/icons.h"
66 #include <gdk/gdkkeysyms.h>
68 #include <glib/gstdio.h>
69 #include <glib/gi18n.h>
71 /* Relax some dependencies */
72 #if ! GLIB_CHECK_VERSION(2,12,0)
73 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
74 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
78 #define VIK_TRW_LAYER_TRACK_GC 13
79 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
80 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
81 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
82 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
84 #define DRAWMODE_BY_TRACK 0
85 #define DRAWMODE_BY_VELOCITY 1
86 #define DRAWMODE_ALL_BLACK 2
91 /* this is how it knows when you click if you are clicking close to a trackpoint. */
92 #define TRACKPOINT_SIZE_APPROX 5
93 #define WAYPOINT_SIZE_APPROX 5
95 #define MIN_STOP_LENGTH 15
96 #define MAX_STOP_LENGTH 86400
97 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
98 /* this is multiplied by user-inputted value from 1-100. */
100 VIK_TRW_LAYER_SUBLAYER_TRACKS,
101 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
102 VIK_TRW_LAYER_SUBLAYER_TRACK,
103 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
106 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
108 struct _VikTrwLayer {
111 GHashTable *tracks_iters;
112 GHashTable *waypoints_iters;
113 GHashTable *waypoints;
114 GtkTreeIter waypoints_iter, tracks_iter;
115 gboolean tracks_visible, waypoints_visible;
118 guint8 drawelevation;
119 guint8 elevation_factor;
123 guint8 line_thickness;
124 guint8 bg_line_thickness;
128 gboolean wp_draw_symbols;
130 gdouble velocity_min, velocity_max;
132 guint16 track_gc_iter;
133 GdkGC *current_track_gc;
136 GdkGC *waypoint_text_gc;
137 GdkGC *waypoint_bg_gc;
138 GdkFont *waypoint_font;
139 VikTrack *current_track;
140 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
141 gboolean ct_sync_done;
144 VikCoordMode coord_mode;
146 /* wp editing tool */
147 VikWaypoint *current_wp;
148 gchar *current_wp_name;
150 gboolean waypoint_rightclick;
152 /* track editing tool */
154 gchar *current_tp_track_name;
155 VikTrwLayerTpwin *tpwin;
157 /* weird hack for joining tracks */
159 gchar *last_tp_track_name;
161 /* track editing tool -- more specifically, moving tps */
164 /* magic scissors tool */
165 gboolean magic_scissors_started;
166 VikCoord magic_scissors_coord;
167 gboolean magic_scissors_check_added_track;
168 gchar *magic_scissors_added_track_name;
169 VikTrack *magic_scissors_current_track;
170 gboolean magic_scissors_append;
177 guint16 image_cache_size;
179 /* for waypoint text */
180 PangoLayout *wplabellayout;
182 gboolean has_verified_thumbnails;
184 GtkMenu *wp_right_click_menu;
187 VikStdLayerMenuItem menu_selection;
189 gint highest_wp_number;
192 /* A caached waypoint image. */
195 gchar *image; /* filename */
198 struct DrawingParams {
202 guint16 width, height;
203 const VikCoord *center;
205 gboolean one_zone, lat_lon;
206 gdouble ce1, ce2, cn1, cn2;
209 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16);
210 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl);
212 static void trw_layer_delete_item ( gpointer *pass_along );
213 static void trw_layer_copy_item_cb( gpointer *pass_along);
214 static void trw_layer_cut_item_cb( gpointer *pass_along);
216 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
217 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
218 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
220 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
221 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
223 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
224 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
225 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
227 static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord );
228 static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] );
229 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
230 static void trw_layer_goto_track_max_speed ( gpointer pass_along[5] );
231 static void trw_layer_goto_track_max_alt ( gpointer pass_along[5] );
232 static void trw_layer_goto_track_min_alt ( gpointer pass_along[5] );
233 static void trw_layer_auto_track_view ( gpointer pass_along[5] );
234 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
235 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
236 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6]);
237 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
238 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
239 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
240 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
241 static void trw_layer_new_wp ( gpointer lav[2] );
242 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
243 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
244 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
245 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
246 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
249 static void trw_layer_properties_item ( gpointer pass_along[5] );
250 static void trw_layer_goto_waypoint ( gpointer pass_along[5] );
251 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] );
253 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] );
254 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] );
255 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
258 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
259 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
261 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
262 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
264 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
265 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
266 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
267 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
268 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
270 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
271 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
272 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
273 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
274 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
276 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
277 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
278 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
279 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
280 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
281 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
282 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
283 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
284 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
285 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
286 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
287 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
288 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
289 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
290 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
291 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
292 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
293 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
294 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
295 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
298 static void cached_pixbuf_free ( CachedPixbuf *cp );
299 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
300 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
302 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
303 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
305 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
307 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
308 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
309 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
311 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
312 static void highest_wp_number_reset(VikTrwLayer *vtl);
313 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
314 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
317 static VikToolInterface trw_layer_tools[] = {
318 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
319 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
321 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
322 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
323 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
325 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
326 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
328 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
329 (VikToolMouseFunc) tool_edit_waypoint_click,
330 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
331 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
333 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
334 (VikToolMouseFunc) tool_edit_trackpoint_click,
335 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
336 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
338 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
339 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
341 { N_("Magic Scissors"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
342 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
344 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
346 /****** PARAMETERS ******/
348 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
349 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
351 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
352 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
355 static VikLayerParamScale params_scales[] = {
356 /* min max step digits */
357 { 1, 10, 1, 0 }, /* line_thickness */
358 { 0.0, 99.0, 1, 2 }, /* velocity_min */
359 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
360 /* 5 * step == how much to turn */
361 { 16, 128, 3.2, 0 }, /* image_size */
362 { 0, 255, 5, 0 }, /* image alpha */
363 { 5, 500, 5, 0 }, /* image cache_size */
364 { 0, 8, 1, 0 }, /* image cache_size */
365 { 1, 64, 1, 0 }, /* wpsize */
366 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
367 { 1, 100, 1, 0 }, /* stop_length */
370 VikLayerParam trw_layer_params[] = {
371 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
372 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
374 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
375 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
376 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
377 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
378 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
380 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
381 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
383 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
384 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
385 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
386 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
387 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
389 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
390 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
391 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
392 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
393 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
394 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
395 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
396 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
398 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
399 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
400 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
401 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
404 enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_DE, PARAM_EF, PARAM_DS, PARAM_SL, PARAM_LT, PARAM_BLT, PARAM_TBGC, PARAM_VMIN, PARAM_VMAX, PARAM_DLA, PARAM_WPC, PARAM_WPTC, PARAM_WPBC, PARAM_WPBA, PARAM_WPSYM, PARAM_WPSIZE, PARAM_WPSYMS, PARAM_DI, PARAM_IS, PARAM_IA, PARAM_ICS, NUM_PARAMS };
407 *** 1) Add to trw_layer_params and enumeration
408 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
411 /****** END PARAMETERS ******/
413 VikLayerInterface vik_trw_layer_interface = {
418 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
422 params_groups, /* params_groups */
423 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
427 (VikLayerFuncCreate) vik_trw_layer_create,
428 (VikLayerFuncRealize) vik_trw_layer_realize,
429 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
430 (VikLayerFuncFree) vik_trw_layer_free,
432 (VikLayerFuncProperties) NULL,
433 (VikLayerFuncDraw) vik_trw_layer_draw,
434 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
436 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
437 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
439 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
440 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
442 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
443 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
444 (VikLayerFuncSublayerTooltip) NULL,
445 (VikLayerFuncLayerTooltip) NULL,
447 (VikLayerFuncMarshall) trw_layer_marshall,
448 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
450 (VikLayerFuncSetParam) trw_layer_set_param,
451 (VikLayerFuncGetParam) trw_layer_get_param,
453 (VikLayerFuncReadFileData) a_gpspoint_read_file,
454 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
456 (VikLayerFuncDeleteItem) trw_layer_del_item,
457 (VikLayerFuncCopyItem) trw_layer_copy_item,
458 (VikLayerFuncPasteItem) trw_layer_paste_item,
459 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
461 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
464 /* for copy & paste (I think?) */
472 GType vik_trw_layer_get_type ()
474 static GType vtl_type = 0;
478 static const GTypeInfo vtl_info =
480 sizeof (VikTrwLayerClass),
481 NULL, /* base_init */
482 NULL, /* base_finalize */
483 NULL, /* class init */
484 NULL, /* class_finalize */
485 NULL, /* class_data */
486 sizeof (VikTrwLayer),
488 NULL /* instance init */
490 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
496 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
498 static gpointer pass_along[5];
504 pass_along[1] = NULL;
505 pass_along[2] = GINT_TO_POINTER (subtype);
506 pass_along[3] = sublayer;
507 pass_along[4] = NULL;
509 trw_layer_delete_item ( pass_along );
512 static void trw_layer_copy_item_cb( gpointer pass_along[5])
514 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
515 gint subtype = GPOINTER_TO_INT (pass_along[2]);
516 gpointer * sublayer = pass_along[3];
520 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
523 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
528 static void trw_layer_cut_item_cb( gpointer pass_along[5])
530 trw_layer_copy_item_cb(pass_along);
531 trw_layer_delete_item(pass_along);
534 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
545 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
547 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
549 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
552 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
553 fi = g_malloc ( *len );
554 fi->len = strlen(sublayer) + 1;
555 memcpy(fi->data, sublayer, fi->len);
556 memcpy(fi->data + fi->len, id, il);
558 *item = (guint8 *)fi;
561 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
563 FlatItem *fi = (FlatItem *) item;
565 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
570 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
571 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
572 vik_trw_layer_add_waypoint ( vtl, name, w );
573 waypoint_convert(name, w, &vtl->coord_mode);
574 // Consider if redraw necessary for the new item
575 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
576 vik_layer_emit_update ( VIK_LAYER(vtl) );
579 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
583 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
584 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
585 vik_trw_layer_add_track ( vtl, name, t );
586 track_convert(name, t, &vtl->coord_mode);
587 // Consider if redraw necessary for the new item
588 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
589 vik_layer_emit_update ( VIK_LAYER(vtl) );
595 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
602 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
606 case PARAM_TV: vtl->tracks_visible = data.b; break;
607 case PARAM_WV: vtl->waypoints_visible = data.b; break;
608 case PARAM_DM: vtl->drawmode = data.u; break;
609 case PARAM_DP: vtl->drawpoints = data.b; break;
610 case PARAM_DE: vtl->drawelevation = data.b; break;
611 case PARAM_DS: vtl->drawstops = data.b; break;
612 case PARAM_DL: vtl->drawlines = data.b; break;
613 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
614 vtl->stop_length = data.u;
616 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
617 vtl->elevation_factor = data.u;
619 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
621 vtl->line_thickness = data.u;
622 trw_layer_new_track_gcs ( vtl, vp );
625 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
627 vtl->bg_line_thickness = data.u;
628 trw_layer_new_track_gcs ( vtl, vp );
633 /* Convert to store internally
634 NB file operation always in internal units (metres per second) */
635 vik_units_speed_t speed_units = a_vik_get_units_speed ();
636 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
637 vtl->velocity_min = data.d;
638 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
639 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
640 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
641 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
644 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
649 /* Convert to store internally
650 NB file operation always in internal units (metres per second) */
651 vik_units_speed_t speed_units = a_vik_get_units_speed ();
652 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
653 vtl->velocity_max = data.d;
654 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
655 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
656 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
657 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
660 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
663 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
664 case PARAM_DLA: vtl->drawlabels = data.b; break;
665 case PARAM_DI: vtl->drawimages = data.b; break;
666 case PARAM_IS: if ( data.u != vtl->image_size )
668 vtl->image_size = data.u;
669 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
670 g_queue_free ( vtl->image_cache );
671 vtl->image_cache = g_queue_new ();
674 case PARAM_IA: vtl->image_alpha = data.u; break;
675 case PARAM_ICS: vtl->image_cache_size = data.u;
676 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
677 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
679 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
680 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
681 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
682 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
683 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
684 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
685 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
690 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
692 VikLayerParamData rv;
695 case PARAM_TV: rv.b = vtl->tracks_visible; break;
696 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
697 case PARAM_DM: rv.u = vtl->drawmode; break;
698 case PARAM_DP: rv.b = vtl->drawpoints; break;
699 case PARAM_DE: rv.b = vtl->drawelevation; break;
700 case PARAM_EF: rv.u = vtl->elevation_factor; break;
701 case PARAM_DS: rv.b = vtl->drawstops; break;
702 case PARAM_SL: rv.u = vtl->stop_length; break;
703 case PARAM_DL: rv.b = vtl->drawlines; break;
704 case PARAM_LT: rv.u = vtl->line_thickness; break;
705 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
708 /* Convert to store internally
709 NB file operation always in internal units (metres per second) */
710 vik_units_speed_t speed_units = a_vik_get_units_speed ();
711 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
712 rv.d = vtl->velocity_min;
713 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
714 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
715 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
716 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
719 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
724 /* Convert to store internally
725 NB file operation always in internal units (metres per second) */
726 vik_units_speed_t speed_units = a_vik_get_units_speed ();
727 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
728 rv.d = vtl->velocity_max;
729 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
730 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
731 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
732 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
735 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
738 case PARAM_DLA: rv.b = vtl->drawlabels; break;
739 case PARAM_DI: rv.b = vtl->drawimages; break;
740 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
741 case PARAM_IS: rv.u = vtl->image_size; break;
742 case PARAM_IA: rv.u = vtl->image_alpha; break;
743 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
744 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
745 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
746 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
747 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
748 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
749 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
750 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
755 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
766 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
767 a_gpx_write_file(vtl, f);
768 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
771 g_file_get_contents(tmpname, &dd, &dl, NULL);
772 *len = sizeof(pl) + pl + dl;
773 *data = g_malloc(*len);
774 memcpy(*data, &pl, sizeof(pl));
775 memcpy(*data + sizeof(pl), pd, pl);
776 memcpy(*data + sizeof(pl) + pl, dd, dl);
785 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
787 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
793 memcpy(&pl, data, sizeof(pl));
795 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
798 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
799 g_critical("couldn't open temp file");
802 fwrite(data, len - pl - sizeof(pl), 1, f);
804 a_gpx_read_file(rv, f);
812 static GList * str_array_to_glist(gchar* data[])
816 for (p = (gpointer)data; *p; p++)
817 gl = g_list_prepend(gl, *p);
818 return(g_list_reverse(gl));
821 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
823 return (strcasecmp(s1, s2) == 0);
826 static guint strcase_hash(gconstpointer v)
828 /* 31 bit hash function */
831 gchar s[128]; /* malloc is too slow for reading big files */
834 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
835 p[i] = toupper(t[i]);
841 for (p += 1; *p != '\0'; p++)
842 h = (h << 5) - h + *p;
848 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
850 if (trw_layer_params[PARAM_DM].widget_data == NULL)
851 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
852 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
853 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
855 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
856 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
858 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
859 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
860 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
861 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
863 /* TODO: constants at top */
864 rv->waypoints_visible = rv->tracks_visible = TRUE;
865 rv->drawmode = drawmode;
866 rv->drawpoints = TRUE;
867 rv->drawstops = FALSE;
868 rv->drawelevation = FALSE;
869 rv->elevation_factor = 30;
870 rv->stop_length = 60;
871 rv->drawlines = TRUE;
872 rv->wplabellayout = NULL;
873 rv->wp_right_click_menu = NULL;
874 rv->waypoint_gc = NULL;
875 rv->waypoint_text_gc = NULL;
876 rv->waypoint_bg_gc = NULL;
878 rv->velocity_max = 5.0;
879 rv->velocity_min = 0.0;
880 rv->line_thickness = 1;
881 rv->bg_line_thickness = 0;
882 rv->current_wp = NULL;
883 rv->current_wp_name = NULL;
884 rv->current_track = NULL;
885 rv->current_tpl = NULL;
886 rv->current_tp_track_name = NULL;
887 rv->moving_tp = FALSE;
888 rv->moving_wp = FALSE;
890 rv->ct_sync_done = TRUE;
892 rv->magic_scissors_started = FALSE;
893 rv->magic_scissors_check_added_track = FALSE;
894 rv->magic_scissors_added_track_name = NULL;
895 rv->magic_scissors_current_track = NULL;
896 rv->magic_scissors_append = FALSE;
898 rv->waypoint_rightclick = FALSE;
900 rv->last_tp_track_name = NULL;
902 rv->image_cache = g_queue_new();
904 rv->image_alpha = 255;
905 rv->image_cache_size = 300;
906 rv->drawimages = TRUE;
907 rv->drawlabels = TRUE;
912 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
914 g_hash_table_destroy(trwlayer->waypoints);
915 g_hash_table_destroy(trwlayer->tracks);
917 /* ODC: replace with GArray */
918 trw_layer_free_track_gcs ( trwlayer );
920 if ( trwlayer->wp_right_click_menu )
921 gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) );
923 if ( trwlayer->wplabellayout != NULL)
924 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
926 if ( trwlayer->waypoint_gc != NULL )
927 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
929 if ( trwlayer->waypoint_text_gc != NULL )
930 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
932 if ( trwlayer->waypoint_bg_gc != NULL )
933 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
935 if ( trwlayer->waypoint_font != NULL )
936 gdk_font_unref ( trwlayer->waypoint_font );
938 if ( trwlayer->tpwin != NULL )
939 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
941 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
942 g_queue_free ( trwlayer->image_cache );
945 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
948 dp->xmpp = vik_viewport_get_xmpp ( vp );
949 dp->ympp = vik_viewport_get_ympp ( vp );
950 dp->width = vik_viewport_get_width ( vp );
951 dp->height = vik_viewport_get_height ( vp );
952 dp->center = vik_viewport_get_center ( vp );
953 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
954 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
959 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
960 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
961 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
963 dp->ce1 = dp->center->east_west-w2;
964 dp->ce2 = dp->center->east_west+w2;
965 dp->cn1 = dp->center->north_south-h2;
966 dp->cn2 = dp->center->north_south+h2;
967 } else if ( dp->lat_lon ) {
968 VikCoord upperleft, bottomright;
969 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
970 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
971 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
972 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
973 dp->ce1 = upperleft.east_west;
974 dp->ce2 = bottomright.east_west;
975 dp->cn1 = bottomright.north_south;
976 dp->cn2 = upperleft.north_south;
979 dp->track_gc_iter = 0;
982 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
984 static gdouble rv = 0;
985 if ( tp1->has_timestamp && tp2->has_timestamp )
987 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
988 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
991 return VIK_TRW_LAYER_TRACK_GC_MIN;
992 else if ( vtl->velocity_min >= vtl->velocity_max )
993 return VIK_TRW_LAYER_TRACK_GC_MAX;
995 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
997 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
998 return VIK_TRW_LAYER_TRACK_GC_MAX;
1002 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1005 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1007 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1008 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1009 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1010 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1013 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1015 /* TODO: this function is a mess, get rid of any redundancy */
1016 GList *list = track->trackpoints;
1018 gboolean useoldvals = TRUE;
1020 gboolean drawpoints;
1022 gboolean drawelevation;
1023 gdouble min_alt, max_alt, alt_diff = 0;
1025 const guint8 tp_size_reg = 2;
1026 const guint8 tp_size_cur = 4;
1029 if ( dp->vtl->drawelevation )
1031 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1032 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1033 alt_diff = max_alt - min_alt;
1036 if ( ! track->visible )
1039 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1040 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1041 trw_layer_draw_track ( name, track, dp, TRUE );
1043 if ( drawing_white_background )
1044 drawpoints = drawstops = FALSE;
1046 drawpoints = dp->vtl->drawpoints;
1047 drawstops = dp->vtl->drawstops;
1050 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1051 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1053 if ( track == dp->vtl->current_track )
1054 main_gc = dp->vtl->current_track_gc;
1056 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1059 int x, y, oldx, oldy;
1060 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1062 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1064 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1066 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1068 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1069 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1075 while ((list = g_list_next(list)))
1077 tp = VIK_TRACKPOINT(list->data);
1078 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1080 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1081 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1082 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1083 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1084 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1086 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1088 if ( drawpoints && ! drawing_white_background )
1091 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1094 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1095 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 );
1098 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 );
1101 if ((!tp->newsegment) && (dp->vtl->drawlines))
1103 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1105 /* UTM only: zone check */
1106 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1107 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1109 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1110 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1111 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1115 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1117 if ( drawing_white_background ) {
1118 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1122 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1123 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1125 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1126 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1130 tmp[1].y = oldy-FIXALTITUDE(list->data);
1132 tmp[2].y = y-FIXALTITUDE(list->next->data);
1137 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1138 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1140 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1141 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1143 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1153 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1155 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1156 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1158 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1159 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1160 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1161 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1164 if ( drawing_white_background )
1165 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1167 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1171 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1172 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1179 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1180 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1181 dp->track_gc_iter = 0;
1184 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1185 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1187 trw_layer_draw_track ( name, track, dp, FALSE );
1190 static void cached_pixbuf_free ( CachedPixbuf *cp )
1192 g_object_unref ( G_OBJECT(cp->pixbuf) );
1193 g_free ( cp->image );
1196 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1198 return strcmp ( cp->image, name );
1201 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1204 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1205 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1206 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1209 GdkPixbuf *sym = NULL;
1210 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1212 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1214 if ( wp->image && dp->vtl->drawimages )
1216 GdkPixbuf *pixbuf = NULL;
1219 if ( dp->vtl->image_alpha == 0)
1222 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1224 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1227 gchar *image = wp->image;
1228 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1229 if ( ! regularthumb )
1231 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1232 image = "\x12\x00"; /* this shouldn't occur naturally. */
1236 CachedPixbuf *cp = NULL;
1237 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1238 if ( dp->vtl->image_size == 128 )
1239 cp->pixbuf = regularthumb;
1242 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1243 g_assert ( cp->pixbuf );
1244 g_object_unref ( G_OBJECT(regularthumb) );
1246 cp->image = g_strdup ( image );
1248 /* needed so 'click picture' tool knows how big the pic is; we don't
1249 * store it in cp because they may have been freed already. */
1250 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1251 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1253 g_queue_push_head ( dp->vtl->image_cache, cp );
1254 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1255 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1257 pixbuf = cp->pixbuf;
1261 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1267 w = gdk_pixbuf_get_width ( pixbuf );
1268 h = gdk_pixbuf_get_height ( pixbuf );
1270 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1272 if ( dp->vtl->image_alpha == 255 )
1273 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1275 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1277 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1281 /* DRAW ACTUAL DOT */
1282 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1283 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 );
1285 else if ( wp == dp->vtl->current_wp ) {
1286 switch ( dp->vtl->wp_symbol ) {
1287 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;
1288 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;
1289 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;
1290 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 );
1291 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 );
1295 switch ( dp->vtl->wp_symbol ) {
1296 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;
1297 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;
1298 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;
1299 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 );
1300 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;
1304 if ( dp->vtl->drawlabels )
1306 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1307 gint label_x, label_y;
1309 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1310 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1311 label_x = x - width/2;
1313 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1315 label_y = y - dp->vtl->wp_size - height - 2;
1317 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1318 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1323 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1325 static struct DrawingParams dp;
1326 g_assert ( l != NULL );
1328 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1331 if ( l->tracks_visible )
1332 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1334 if (l->waypoints_visible)
1335 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1338 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1341 if ( vtl->track_bg_gc )
1343 g_object_unref ( vtl->track_bg_gc );
1344 vtl->track_bg_gc = NULL;
1346 if ( vtl->current_track_gc )
1348 g_object_unref ( vtl->current_track_gc );
1349 vtl->current_track_gc = NULL;
1352 if ( ! vtl->track_gc )
1354 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1355 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1356 g_array_free ( vtl->track_gc, TRUE );
1357 vtl->track_gc = NULL;
1360 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1362 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1363 gint width = vtl->line_thickness;
1365 if ( vtl->track_gc )
1366 trw_layer_free_track_gcs ( vtl );
1368 if ( vtl->track_bg_gc )
1369 g_object_unref ( vtl->track_bg_gc );
1370 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1372 if ( vtl->current_track_gc )
1373 g_object_unref ( vtl->current_track_gc );
1374 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1375 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1377 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1379 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1381 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1382 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1383 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1384 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1385 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1386 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1387 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1388 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1389 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1390 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1392 gc[11] = vik_viewport_new_gc ( vp, "#ff0000", width ); /* above range */
1394 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1396 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1399 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1401 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1402 PangoFontDescription *pfd;
1403 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1404 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1405 pango_layout_set_font_description (rv->wplabellayout, pfd);
1406 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1407 pango_font_description_free (pfd);
1409 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1411 trw_layer_new_track_gcs ( rv, vp );
1413 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1414 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1415 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1416 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1418 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1420 rv->has_verified_thumbnails = FALSE;
1421 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1423 rv->wp_draw_symbols = TRUE;
1425 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1427 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1432 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] )
1434 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1436 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1437 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1439 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE );
1442 *new_iter = *((GtkTreeIter *) pass_along[1]);
1443 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1445 if ( ! track->visible )
1446 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1449 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] )
1451 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1452 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1453 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1455 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE );
1458 *new_iter = *((GtkTreeIter *) pass_along[1]);
1459 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1461 if ( ! wp->visible )
1462 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1466 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1469 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1471 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1472 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1474 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1476 if ( ! vtl->tracks_visible )
1477 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1479 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1481 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1482 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1484 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1487 if ( ! vtl->waypoints_visible )
1488 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1490 pass_along[0] = &(vtl->waypoints_iter);
1491 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1493 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1497 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1501 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1502 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1503 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1505 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1507 return (t->visible ^= 1);
1511 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1513 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1515 return (t->visible ^= 1);
1523 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1528 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1530 return l->waypoints;
1533 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1535 static VikCoord fixme;
1536 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1537 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1538 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1539 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1540 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1541 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1542 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1543 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1544 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1547 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1550 static VikCoord fixme;
1554 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1555 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1556 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1557 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1558 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1559 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1560 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1561 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1562 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1567 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1569 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1570 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1572 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1573 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1574 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1575 maxmin[0].lat = wpt_maxmin[0].lat;
1578 maxmin[0].lat = trk_maxmin[0].lat;
1580 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1581 maxmin[0].lon = wpt_maxmin[0].lon;
1584 maxmin[0].lon = trk_maxmin[0].lon;
1586 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1587 maxmin[1].lat = wpt_maxmin[1].lat;
1590 maxmin[1].lat = trk_maxmin[1].lat;
1592 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1593 maxmin[1].lon = wpt_maxmin[1].lon;
1596 maxmin[1].lon = trk_maxmin[1].lon;
1600 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1602 /* 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... */
1603 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1604 trw_layer_find_maxmin (vtl, maxmin);
1605 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1609 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1610 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1615 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1618 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1619 goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord );
1621 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1624 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1626 /* First set the center [in case previously viewing from elsewhere] */
1627 /* Then loop through zoom levels until provided positions are in view */
1628 /* This method is not particularly fast - but should work well enough */
1629 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1631 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1632 vik_viewport_set_center_coord ( vvp, &coord );
1634 /* Convert into definite 'smallest' and 'largest' positions */
1635 struct LatLon minmin;
1636 if ( maxmin[0].lat < maxmin[1].lat )
1637 minmin.lat = maxmin[0].lat;
1639 minmin.lat = maxmin[1].lat;
1641 struct LatLon maxmax;
1642 if ( maxmin[0].lon > maxmin[1].lon )
1643 maxmax.lon = maxmin[0].lon;
1645 maxmax.lon = maxmin[1].lon;
1647 /* Never zoom in too far - generally not that useful, as too close ! */
1648 /* Always recalculate the 'best' zoom level */
1650 vik_viewport_set_zoom ( vvp, zoom );
1652 gdouble min_lat, max_lat, min_lon, max_lon;
1653 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1654 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1655 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1656 /* NB I think the logic used in this test to determine if the bounds is within view
1657 fails if track goes across 180 degrees longitude.
1658 Hopefully that situation is not too common...
1659 Mind you viking doesn't really do edge locations to well anyway */
1660 if ( min_lat < minmin.lat &&
1661 max_lat > minmin.lat &&
1662 min_lon < maxmax.lon &&
1663 max_lon > maxmax.lon )
1664 /* Found within zoom level */
1669 vik_viewport_set_zoom ( vvp, zoom );
1673 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
1675 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
1676 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1677 trw_layer_find_maxmin (vtl, maxmin);
1678 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1681 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
1686 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
1688 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])) ) ) {
1689 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1692 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1695 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
1697 GtkWidget *file_selector;
1699 gboolean failed = FALSE;
1700 file_selector = gtk_file_chooser_dialog_new (title,
1702 GTK_FILE_CHOOSER_ACTION_SAVE,
1703 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1704 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1706 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
1708 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
1710 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
1711 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
1713 gtk_widget_hide ( file_selector );
1714 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
1719 if ( a_dialog_overwrite ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
1721 gtk_widget_hide ( file_selector );
1722 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
1727 gtk_widget_destroy ( file_selector );
1729 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
1732 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
1734 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
1737 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
1739 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
1742 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
1744 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX );
1747 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
1749 gpointer layer_and_vlp[2];
1750 layer_and_vlp[0] = pass_along[0];
1751 layer_and_vlp[1] = pass_along[1];
1753 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
1754 gchar *auto_save_name = g_strdup ( pass_along[3] );
1755 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
1756 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
1758 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
1760 g_free ( auto_save_name );
1763 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
1765 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
1766 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
1767 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
1768 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1770 GTK_RESPONSE_REJECT,
1772 GTK_RESPONSE_ACCEPT,
1775 GtkWidget *label, *entry;
1776 label = gtk_label_new(_("Waypoint Name:"));
1777 entry = gtk_entry_new();
1779 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
1780 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
1781 gtk_widget_show_all ( label );
1782 gtk_widget_show_all ( entry );
1784 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
1786 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
1789 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
1792 for ( i = strlen(upname)-1; i >= 0; i-- )
1793 upname[i] = toupper(upname[i]);
1795 wp = g_hash_table_lookup ( wps, upname );
1798 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
1801 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
1802 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1803 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, upname ) );
1810 gtk_widget_destroy ( dia );
1813 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
1815 gchar *name = highest_wp_number_get(vtl);
1816 VikWaypoint *wp = vik_waypoint_new();
1817 wp->coord = *def_coord;
1819 if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) )
1822 vik_trw_layer_add_waypoint ( vtl, name, wp );
1825 vik_waypoint_free(wp);
1829 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
1832 struct LatLon one_ll, two_ll;
1833 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1835 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1836 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1837 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
1838 VikViewport *vvp = vik_window_viewport(vw);
1839 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
1840 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
1841 vik_coord_to_latlon(&one, &one_ll);
1842 vik_coord_to_latlon(&two, &two_ll);
1843 if (one_ll.lat > two_ll.lat) {
1844 maxmin[0].lat = one_ll.lat;
1845 maxmin[1].lat = two_ll.lat;
1848 maxmin[0].lat = two_ll.lat;
1849 maxmin[1].lat = one_ll.lat;
1851 if (one_ll.lon > two_ll.lon) {
1852 maxmin[0].lon = one_ll.lon;
1853 maxmin[1].lon = two_ll.lon;
1856 maxmin[0].lon = two_ll.lon;
1857 maxmin[1].lon = one_ll.lon;
1859 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
1862 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
1864 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1865 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1866 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1868 trw_layer_find_maxmin (vtl, maxmin);
1869 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
1872 static void trw_layer_new_wp ( gpointer lav[2] )
1874 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1875 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1876 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
1877 instead return true if you want to update. */
1878 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 )
1879 vik_layers_panel_emit_update ( vlp );
1882 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
1884 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1885 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1887 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1888 struct LatLon maxmin[2] = { {0,0}, {0,0} };
1889 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
1890 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
1891 vik_layers_panel_emit_update ( vlp );
1895 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
1897 /* NB do not care if wp is visible or not */
1898 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
1901 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
1903 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1904 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1906 /* Only 1 waypoint - jump straight to it */
1907 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
1908 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
1909 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
1911 /* If at least 2 waypoints - find center and then zoom to fit */
1912 else if ( g_hash_table_size (vtl->waypoints) > 1 )
1914 struct LatLon maxmin[2] = { {0,0}, {0,0} };
1915 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
1916 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
1919 vik_layers_panel_emit_update ( vlp );
1922 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
1924 static gpointer pass_along[2];
1926 GtkWidget *export_submenu;
1927 GtkWidget *wikipedia_submenu;
1928 pass_along[0] = vtl;
1929 pass_along[1] = vlp;
1931 item = gtk_menu_item_new();
1932 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1933 gtk_widget_show ( item );
1935 item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") );
1936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
1937 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1938 gtk_widget_show ( item );
1940 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
1941 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
1942 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1943 gtk_widget_show ( item );
1945 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
1946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
1947 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1948 gtk_widget_show ( item );
1950 item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
1951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
1952 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1953 gtk_widget_show ( item );
1955 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint") );
1956 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
1957 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1958 gtk_widget_show ( item );
1960 export_submenu = gtk_menu_new ();
1961 item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
1962 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1963 gtk_widget_show ( item );
1964 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1966 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point") );
1967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
1968 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1969 gtk_widget_show ( item );
1971 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper") );
1972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1973 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1974 gtk_widget_show ( item );
1976 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX") );
1977 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1978 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1979 gtk_widget_show ( item );
1981 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
1982 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
1983 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1984 gtk_widget_show ( item );
1986 #ifdef VIK_CONFIG_GEONAMES
1987 wikipedia_submenu = gtk_menu_new();
1988 item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
1989 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
1990 gtk_widget_show(item);
1991 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
1993 item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
1994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
1995 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
1996 gtk_widget_show ( item );
1998 item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
1999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2000 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2001 gtk_widget_show ( item );
2004 #ifdef VIK_CONFIG_OPENSTREETMAP
2005 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
2006 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2007 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2008 gtk_widget_show ( item );
2011 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2012 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2014 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2015 gtk_widget_show ( item );
2018 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2019 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2021 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2022 gtk_widget_show ( item );
2026 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2028 if ( VIK_LAYER(vtl)->realized )
2030 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2032 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2035 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2036 // Visibility column always needed for waypoints
2037 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2038 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2040 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2042 // Actual setting of visibility dependent on the waypoint
2043 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2044 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2045 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2049 highest_wp_number_add_wp(vtl, name);
2050 g_hash_table_insert ( vtl->waypoints, name, wp );
2054 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2056 if ( VIK_LAYER(vtl)->realized )
2058 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2060 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2063 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2064 // Visibility column always needed for tracks
2065 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2066 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2068 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2070 // Actual setting of visibility dependent on the track
2071 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2072 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2073 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2077 g_hash_table_insert ( vtl->tracks, name, t );
2081 /* to be called whenever a track has been deleted or may have been changed. */
2082 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2084 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2085 trw_layer_cancel_current_tp ( vtl, FALSE );
2086 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2087 trw_layer_cancel_last_tp ( vtl );
2090 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2093 gchar *newname = g_strdup(name);
2094 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2095 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2096 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2098 newname = new_newname;
2104 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2106 vik_trw_layer_add_waypoint ( vtl,
2107 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2110 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2112 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
2113 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2114 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2115 vik_track_free ( tr );
2116 vtl->magic_scissors_append = FALSE; /* this means we have added it */
2118 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2119 vik_trw_layer_add_track ( vtl, new_name, tr );
2121 if ( vtl->magic_scissors_check_added_track ) {
2122 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2123 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2124 g_free ( vtl->magic_scissors_added_track_name );
2125 vtl->magic_scissors_added_track_name = g_strdup(new_name);
2130 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2132 *l = g_list_append(*l, (gpointer)name);
2135 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2137 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2138 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2140 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2141 vik_trw_layer_delete_track(vtl_src, name);
2142 vik_trw_layer_add_track(vtl_dest, newname, t);
2144 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2146 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2147 vik_trw_layer_delete_waypoint(vtl_src, name);
2148 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2152 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2154 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2155 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2157 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2158 GList *items = NULL;
2161 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2162 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2164 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2165 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2170 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2171 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2173 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2180 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2181 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2186 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2188 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2189 gboolean was_visible = FALSE;
2193 was_visible = t->visible;
2194 if ( t == vtl->current_track )
2195 vtl->current_track = NULL;
2196 if ( t == vtl->magic_scissors_current_track )
2197 vtl->magic_scissors_current_track = NULL;
2199 /* could be current_tp, so we have to check */
2200 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2202 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2203 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2204 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2206 /* do this last because trk_name may be pointing to actual orig key */
2207 g_hash_table_remove ( vtl->tracks, trk_name );
2212 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2214 gboolean was_visible = FALSE;
2217 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2221 if ( wp == vtl->current_wp ) {
2222 vtl->current_wp = NULL;
2223 vtl->current_wp_name = NULL;
2224 vtl->moving_wp = FALSE;
2227 was_visible = wp->visible;
2228 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2229 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2230 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2232 highest_wp_number_remove_wp(vtl, wp_name);
2233 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2239 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2241 vik_treeview_item_delete (vt, it );
2244 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2247 vtl->current_track = NULL;
2248 vtl->magic_scissors_current_track = NULL;
2249 if (vtl->current_tp_track_name)
2250 trw_layer_cancel_current_tp(vtl, FALSE);
2251 if (vtl->last_tp_track_name)
2252 trw_layer_cancel_last_tp ( vtl );
2254 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2255 g_hash_table_remove_all(vtl->tracks_iters);
2256 g_hash_table_remove_all(vtl->tracks);
2258 /* TODO: only update if the layer is visible (ticked) */
2259 vik_layer_emit_update ( VIK_LAYER(vtl) );
2262 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2264 vtl->current_wp = NULL;
2265 vtl->current_wp_name = NULL;
2266 vtl->moving_wp = FALSE;
2268 highest_wp_number_reset(vtl);
2270 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2271 g_hash_table_remove_all(vtl->waypoints_iters);
2272 g_hash_table_remove_all(vtl->waypoints);
2274 /* TODO: only update if the layer is visible (ticked) */
2275 vik_layer_emit_update ( VIK_LAYER(vtl) );
2278 static void trw_layer_delete_item ( gpointer pass_along[5] )
2280 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2281 gboolean was_visible = FALSE;
2282 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2284 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2288 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2291 vik_layer_emit_update ( VIK_LAYER(vtl) );
2295 static void trw_layer_properties_item ( gpointer pass_along[5] )
2297 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2298 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2300 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2303 if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) )
2305 if ( VIK_LAYER(vtl)->visible )
2306 vik_layer_emit_update ( VIK_LAYER(vtl) );
2311 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2314 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2316 pass_along[1], /* vlp */
2317 pass_along[3] /* track name */);
2322 static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord )
2324 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord );
2325 vik_layers_panel_emit_update ( vlp );
2328 static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] )
2330 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2331 if ( trps && trps->data )
2332 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2335 static void trw_layer_goto_track_center ( gpointer pass_along[5] )
2337 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2338 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2339 if ( trps && *trps )
2341 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2343 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2344 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2345 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2346 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2347 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord);
2351 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2353 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2354 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2356 vtl->current_track = track;
2357 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2359 if ( track->trackpoints )
2360 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2364 * extend a track using magic scissors
2366 static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2368 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2369 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2370 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2372 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2373 vtl->magic_scissors_coord = last_coord;
2374 vtl->magic_scissors_current_track = track;
2375 vtl->magic_scissors_started = TRUE;
2377 if ( track->trackpoints )
2378 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &last_coord) ;
2382 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2384 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2385 /* Also warn if overwrite old elevation data */
2386 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2388 vik_track_apply_dem_data ( track );
2391 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2393 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2396 trps = g_list_last(trps);
2397 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2400 static void trw_layer_goto_track_max_speed ( gpointer pass_along[5] )
2402 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2405 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2408 static void trw_layer_goto_track_max_alt ( gpointer pass_along[5] )
2410 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2413 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2416 static void trw_layer_goto_track_min_alt ( gpointer pass_along[5] )
2418 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2421 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2425 * Automatically change the viewport to center on the track and zoom to see the extent of the track
2427 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2429 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2430 if ( trps && *trps )
2432 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2433 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2435 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(pass_along[1])), maxmin );
2436 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2440 /*************************************
2441 * merge/split by time routines
2442 *************************************/
2444 /* called for each key in track hash table.
2445 * If the current track has time stamp, add it to the result,
2446 * except the one pointed by "exclude".
2447 * set exclude to NULL if there is no exclude to check.
2448 * Not that result is in reverse (for performance reason).
2454 static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2456 twt_udata *user_data = udata;
2457 VikTrackpoint *p1, *p2;
2459 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2463 if (VIK_TRACK(value)->trackpoints) {
2464 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2465 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2467 if (!p1->has_timestamp || !p2->has_timestamp) {
2468 g_print("no timestamp\n");
2474 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2477 /* called for each key in track hash table. if original track user_data[1] is close enough
2478 * to the passed one, add it to list in user_data[0]
2480 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2483 VikTrackpoint *p1, *p2;
2485 GList **nearby_tracks = ((gpointer *)user_data)[0];
2486 GList *orig_track = ((gpointer *)user_data)[1];
2487 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
2490 * detect reasons for not merging, and return
2491 * if no reason is found not to merge, then do it.
2494 if (VIK_TRACK(value)->trackpoints == orig_track) {
2498 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2499 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2501 if (VIK_TRACK(value)->trackpoints) {
2502 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2503 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2505 if (!p1->has_timestamp || !p2->has_timestamp) {
2506 g_print("no timestamp\n");
2510 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2511 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2513 abs(p1->timestamp - t2) < thr*60)
2520 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2523 /* comparison function used to sort tracks; a and b are hash table keys */
2524 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2526 GHashTable *tracks = user_data;
2529 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2530 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2532 if (t1 < t2) return -1;
2533 if (t1 > t2) return 1;
2537 /* comparison function used to sort trackpoints */
2538 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2540 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2542 if (t1 < t2) return -1;
2543 if (t1 > t2) return 1;
2547 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2549 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2550 gchar *orig_track_name = pass_along[3];
2551 GList *tracks_with_timestamp = NULL;
2552 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2554 if (track->trackpoints &&
2555 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2556 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2563 udata.result = &tracks_with_timestamp;
2564 udata.exclude = track->trackpoints;
2565 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2566 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2569 if (!tracks_with_timestamp) {
2570 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2574 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2575 vtl->tracks, tracks_with_timestamp, TRUE,
2576 _("Merge with..."), _("Select track to merge with"));
2577 g_list_free(tracks_with_timestamp);
2582 for (l = merge_list; l != NULL; l = g_list_next(l)) {
2583 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2585 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2586 merge_track->trackpoints = NULL;
2587 vik_trw_layer_delete_track(vtl, l->data);
2588 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2591 /* TODO: free data before free merge_list */
2592 for (l = merge_list; l != NULL; l = g_list_next(l))
2594 g_list_free(merge_list);
2595 vik_layer_emit_update( VIK_LAYER(vtl) );
2599 /* merge by time routine */
2600 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2602 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2603 gchar *orig_track_name = strdup(pass_along[3]);
2606 GList *nearby_tracks;
2609 static guint thr = 1;
2610 guint track_count = 0;
2612 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2613 _("Merge Threshold..."),
2614 _("Merge when time between tracks less than:"),
2616 free(orig_track_name);
2620 /* merge tracks until we can't */
2621 nearby_tracks = NULL;
2625 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2626 trps = track->trackpoints;
2631 if (nearby_tracks) {
2632 g_list_free(nearby_tracks);
2633 nearby_tracks = NULL;
2636 t1 = ((VikTrackpoint *)trps->data)->timestamp;
2637 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
2639 /* g_print("Original track times: %d and %d\n", t1, t2); */
2640 params[0] = &nearby_tracks;
2642 params[2] = GUINT_TO_POINTER (thr);
2644 /* get a list of adjacent-in-time tracks */
2645 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
2647 /* add original track */
2648 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
2652 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
2653 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
2654 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
2655 GList *l = nearby_tracks;
2656 VikTrack *tr = vik_track_new();
2657 tr->visible = track->visible;
2662 t1 = get_first_trackpoint(l)->timestamp;
2663 t2 = get_last_trackpoint(l)->timestamp;
2664 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
2668 /* remove trackpoints from merged track, delete track */
2669 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
2670 get_track(l)->trackpoints = NULL;
2671 vik_trw_layer_delete_track(vtl, l->data);
2676 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
2677 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
2679 #undef get_first_trackpoint
2680 #undef get_last_trackpoint
2683 } while (track_count > 1);
2684 g_list_free(nearby_tracks);
2685 free(orig_track_name);
2686 vik_layer_emit_update( VIK_LAYER(vtl) );
2689 /* split by time routine */
2690 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
2692 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2693 GList *trps = track->trackpoints;
2695 GList *newlists = NULL;
2696 GList *newtps = NULL;
2698 static guint thr = 1;
2705 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2706 _("Split Threshold..."),
2707 _("Split when time between trackpoints exceeds:"),
2712 /* iterate through trackpoints, and copy them into new lists without touching original list */
2713 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
2717 ts = VIK_TRACKPOINT(iter->data)->timestamp;
2719 g_print("panic: ts < prev_ts: this should never happen!\n");
2722 if (ts - prev_ts > thr*60) {
2723 /* flush accumulated trackpoints into new list */
2724 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2728 /* accumulate trackpoint copies in newtps, in reverse order */
2729 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
2731 iter = g_list_next(iter);
2734 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2737 /* put lists of trackpoints into tracks */
2744 tr = vik_track_new();
2745 tr->visible = track->visible;
2746 tr->trackpoints = (GList *)(iter->data);
2748 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
2749 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
2750 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
2751 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
2753 iter = g_list_next(iter);
2755 g_list_free(newlists);
2756 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
2757 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2760 /* end of split/merge routines */
2763 static void trw_layer_goto_waypoint ( gpointer pass_along[5] )
2765 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2767 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) );
2770 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] )
2772 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
2773 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
2777 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2779 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2784 if (strcmp(newname, sublayer) == 0 )
2787 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
2788 if (g_hash_table_lookup( l->waypoints, newname))
2790 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
2795 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
2796 g_hash_table_steal ( l->waypoints_iters, sublayer );
2798 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
2799 highest_wp_number_remove_wp(l, sublayer);
2800 g_hash_table_remove ( l->waypoints, sublayer );
2802 rv = g_strdup(newname);
2804 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2806 highest_wp_number_add_wp(l, rv);
2807 g_hash_table_insert ( l->waypoints, rv, wp );
2808 g_hash_table_insert ( l->waypoints_iters, rv, iter );
2810 /* it hasn't been updated yet so we pass new name */
2811 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2812 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2815 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2818 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2825 if (strcmp(newname, sublayer) == 0)
2828 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
2829 if (g_hash_table_lookup( l->tracks, newname))
2831 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
2836 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
2837 g_hash_table_steal ( l->tracks, sublayer );
2839 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
2840 g_hash_table_steal ( l->tracks_iters, sublayer );
2842 rv = g_strdup(newname);
2844 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2846 g_hash_table_insert ( l->tracks, rv, tr );
2847 g_hash_table_insert ( l->tracks_iters, rv, iter );
2849 /* don't forget about current_tp_track_name, update that too */
2850 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
2852 l->current_tp_track_name = rv;
2854 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
2856 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
2857 l->last_tp_track_name = rv;
2859 g_free ( orig_key );
2861 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2862 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2865 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2871 static gboolean is_valid_geocache_name ( gchar *str )
2873 gint len = strlen ( str );
2874 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]));
2877 static void trw_layer_track_use_with_filter ( gpointer *pass_along )
2879 gchar *track_name = (gchar *) pass_along[3];
2880 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
2881 a_acquire_set_filter_track ( tr, track_name );
2884 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
2886 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
2887 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
2890 static void trw_layer_track_google_route_webpage( gpointer *pass_along )
2892 gchar *track_name = (gchar *) pass_along[3];
2893 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
2895 gchar *escaped = uri_escape ( tr->comment );
2896 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
2897 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
2903 /* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */
2904 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2906 static GtkTreeIter staticiter;
2907 static gpointer pass_along[5];
2909 gboolean rv = FALSE;
2912 pass_along[1] = vlp;
2913 pass_along[2] = GINT_TO_POINTER (subtype);
2914 pass_along[3] = sublayer;
2915 staticiter = *iter; /* will exist after function has ended */
2916 pass_along[4] = &staticiter;
2918 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2922 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
2923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
2924 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2925 gtk_widget_show ( item );
2927 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2928 VikTrwLayer *vtl = l;
2929 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
2930 if (tr && tr->property_dialog)
2931 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
2934 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
2935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
2936 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2937 gtk_widget_show ( item );
2939 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
2940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
2941 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2942 gtk_widget_show ( item );
2944 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
2945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
2946 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2947 gtk_widget_show ( item );
2949 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2951 /* could be a right-click using the tool */
2952 if ( vlp != NULL ) {
2953 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
2954 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
2955 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2956 gtk_widget_show ( item );
2959 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
2961 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
2962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
2963 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2964 gtk_widget_show ( item );
2970 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2972 GtkWidget *goto_submenu;
2973 item = gtk_menu_item_new ();
2974 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2975 gtk_widget_show ( item );
2977 goto_submenu = gtk_menu_new ();
2978 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
2979 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2980 gtk_widget_show ( item );
2981 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
2983 item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
2984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
2985 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2986 gtk_widget_show ( item );
2988 item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
2989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
2990 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2991 gtk_widget_show ( item );
2993 item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
2994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
2995 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2996 gtk_widget_show ( item );
2998 item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
2999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3000 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3001 gtk_widget_show ( item );
3003 item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3005 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3006 gtk_widget_show ( item );
3008 item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3010 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3011 gtk_widget_show ( item );
3013 item = gtk_menu_item_new_with_mnemonic ( _("_View Track") );
3014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3015 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3016 gtk_widget_show ( item );
3018 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time") );
3019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3020 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3021 gtk_widget_show ( item );
3023 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3025 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3026 gtk_widget_show ( item );
3028 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time") );
3029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3030 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3031 gtk_widget_show ( item );
3033 item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3035 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3036 gtk_widget_show ( item );
3038 item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3039 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3040 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3041 gtk_widget_show ( item );
3043 item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX") );
3044 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3045 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3046 gtk_widget_show ( item );
3048 item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3049 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3050 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3051 gtk_widget_show ( item );
3053 item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Magic Scissors") );
3054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
3055 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3056 gtk_widget_show ( item );
3058 #ifdef VIK_CONFIG_OPENSTREETMAP
3059 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
3060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3061 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3062 gtk_widget_show ( item );
3065 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3067 item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3069 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3070 gtk_widget_show ( item );
3073 item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
3074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3075 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3076 gtk_widget_show ( item );
3078 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3079 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3080 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3082 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3083 gtk_widget_show ( item );
3087 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3089 item = gtk_menu_item_new ();
3090 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3091 gtk_widget_show ( item );
3093 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
3094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3095 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3096 gtk_widget_show ( item );
3102 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3105 if (!vtl->current_tpl)
3107 if (!vtl->current_tpl->next)
3110 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3111 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3113 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3116 VikTrackpoint *tp_new = vik_trackpoint_new();
3117 struct LatLon ll_current, ll_next;
3118 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3119 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3121 /* main positional interpolation */
3122 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3123 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3125 /* Now other properties that can be interpolated */
3126 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3128 if (tp_current->has_timestamp && tp_next->has_timestamp) {
3129 /* Note here the division is applied to each part, then added
3130 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3131 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3132 tp_new->has_timestamp = TRUE;
3135 if (tp_current->speed != NAN && tp_next->speed != NAN)
3136 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3138 /* TODO - improve interpolation of course, as it may not be correct.
3139 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3140 [similar applies if value is in radians] */
3141 if (tp_current->course != NAN && tp_next->course != NAN)
3142 tp_new->speed = (tp_current->course + tp_next->course)/2;
3144 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3146 /* Insert new point into the trackpoints list after the current TP */
3147 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3148 gint index = g_list_index ( tr->trackpoints, tp_current );
3150 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3155 /* to be called when last_tpl no long exists. */
3156 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3158 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3159 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3160 vtl->last_tpl = NULL;
3161 vtl->last_tp_track_name = NULL;
3164 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3170 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3174 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3176 if ( vtl->current_tpl )
3178 vtl->current_tpl = NULL;
3179 vtl->current_tp_track_name = NULL;
3180 vik_layer_emit_update(VIK_LAYER(vtl));
3184 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3186 g_assert ( vtl->tpwin != NULL );
3187 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3188 trw_layer_cancel_current_tp ( vtl, TRUE );
3189 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3192 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, NULL ) ) )
3194 VikTrack *tr = vik_track_new ();
3195 GList *newglist = g_list_alloc ();
3196 newglist->prev = NULL;
3197 newglist->next = vtl->current_tpl->next;
3198 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3199 tr->trackpoints = newglist;
3201 vtl->current_tpl->next->prev = newglist; /* end old track here */
3202 vtl->current_tpl->next = NULL;
3204 vtl->current_tpl = newglist; /* change tp to first of new track. */
3205 vtl->current_tp_track_name = name;
3207 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3211 vik_trw_layer_add_track ( vtl, name, tr );
3212 vik_layer_emit_update(VIK_LAYER(vtl));
3215 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3217 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3219 g_assert(tr != NULL);
3221 /* can't join with a non-existent trackpoint */
3222 vtl->last_tpl = NULL;
3223 vtl->last_tp_track_name = NULL;
3225 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3227 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3228 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3230 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3232 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3233 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3235 trw_layer_cancel_last_tp ( vtl );
3237 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3238 g_list_free_1 ( vtl->current_tpl );
3239 vtl->current_tpl = new_tpl;
3240 vik_layer_emit_update(VIK_LAYER(vtl));
3244 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3245 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3246 g_list_free_1 ( vtl->current_tpl );
3247 trw_layer_cancel_current_tp ( vtl, FALSE );
3250 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3252 vtl->last_tpl = vtl->current_tpl;
3253 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3254 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3256 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3258 vtl->last_tpl = vtl->current_tpl;
3259 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3260 vik_layer_emit_update(VIK_LAYER(vtl));
3262 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
3264 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
3265 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3267 VikTrack *tr_first = tr1, *tr_last = tr2;
3271 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
3272 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
3273 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
3274 vik_track_reverse ( tr1 );
3275 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
3280 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
3282 if ( tr_last->trackpoints ) /* deleting this part here joins them into 1 segmented track. useful but there's no place in the UI for this feature. segments should be deprecated anyway. */
3283 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
3284 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
3285 tr2->trackpoints = NULL;
3287 tmp = vtl->current_tp_track_name;
3289 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
3290 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3292 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
3293 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
3294 vik_trw_layer_delete_track ( vtl, tmp );
3296 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
3297 vik_layer_emit_update(VIK_LAYER(vtl));
3299 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
3301 trw_layer_insert_tp_after_current_tp ( vtl );
3302 vik_layer_emit_update(VIK_LAYER(vtl));
3304 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
3305 vik_layer_emit_update (VIK_LAYER(vtl));
3308 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
3312 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
3313 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
3314 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
3315 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
3316 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
3318 if ( vtl->current_tpl )
3319 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3320 /* set layer name and TP data */
3323 /***************************************************************************
3325 ***************************************************************************/
3327 /*** Utility data structures and functions ****/
3331 gint closest_x, closest_y;
3332 gchar *closest_wp_name;
3333 VikWaypoint *closest_wp;
3339 gint closest_x, closest_y;
3340 gchar *closest_track_name;
3341 VikTrackpoint *closest_tp;
3346 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
3352 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
3354 if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
3355 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
3356 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3358 params->closest_wp_name = name;
3359 params->closest_wp = wp;
3360 params->closest_x = x;
3361 params->closest_y = y;
3365 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
3367 GList *tpl = t->trackpoints;
3376 tp = VIK_TRACKPOINT(tpl->data);
3378 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
3380 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
3381 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
3382 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3384 params->closest_track_name = name;
3385 params->closest_tp = tp;
3386 params->closest_tpl = tpl;
3387 params->closest_x = x;
3388 params->closest_y = y;
3394 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3396 TPSearchParams params;
3400 params.closest_track_name = NULL;
3401 params.closest_tp = NULL;
3402 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3403 return params.closest_tp;
3406 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3408 WPSearchParams params;
3412 params.closest_wp = NULL;
3413 params.closest_wp_name = NULL;
3414 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3415 return params.closest_wp;
3418 /* background drawing hook, to be passed the viewport */
3419 static gboolean tool_sync_done = TRUE;
3421 static gboolean tool_sync(gpointer data)
3423 VikViewport *vvp = data;
3424 gdk_threads_enter();
3425 vik_viewport_sync(vvp);
3426 tool_sync_done = TRUE;
3427 gdk_threads_leave();
3438 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
3441 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
3442 gdk_gc_set_function ( t->gc, GDK_INVERT );
3443 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3444 vik_viewport_sync(t->vvp);
3449 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
3451 VikViewport *vvp = t->vvp;
3452 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3453 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3457 if (tool_sync_done) {
3458 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
3459 tool_sync_done = FALSE;
3463 static void marker_end_move ( tool_ed_t *t )
3465 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3466 g_object_unref ( t->gc );
3470 /*** Edit waypoint ****/
3472 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3474 tool_ed_t *t = g_new(tool_ed_t, 1);
3480 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3482 WPSearchParams params;
3483 tool_ed_t *t = data;
3484 VikViewport *vvp = t->vvp;
3486 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3493 if ( !vtl->vl.visible || !vtl->waypoints_visible )
3496 if ( vtl->current_wp && vtl->current_wp->visible )
3498 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
3500 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
3502 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
3503 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
3505 if ( event->button == 3 )
3506 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3508 marker_begin_move(t, event->x, event->y);
3515 params.x = event->x;
3516 params.y = event->y;
3517 params.closest_wp_name = NULL;
3518 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3519 params.closest_wp = NULL;
3520 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3521 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
3523 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
3524 marker_begin_move(t, event->x, event->y);
3525 g_critical("shouldn't be here");
3528 else if ( params.closest_wp )
3530 if ( event->button == 3 )
3531 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3533 vtl->waypoint_rightclick = FALSE;
3535 vtl->current_wp = params.closest_wp;
3536 vtl->current_wp_name = params.closest_wp_name;
3538 if ( params.closest_wp )
3539 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
3541 /* could make it so don't update if old WP is off screen and new is null but oh well */
3542 vik_layer_emit_update ( VIK_LAYER(vtl) );
3546 vtl->current_wp = NULL;
3547 vtl->current_wp_name = NULL;
3548 vtl->waypoint_rightclick = FALSE;
3549 vik_layer_emit_update ( VIK_LAYER(vtl) );
3553 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
3555 tool_ed_t *t = data;
3556 VikViewport *vvp = t->vvp;
3558 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3563 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3566 if ( event->state & GDK_CONTROL_MASK )
3568 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3570 new_coord = tp->coord;
3574 if ( event->state & GDK_SHIFT_MASK )
3576 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3577 if ( wp && wp != vtl->current_wp )
3578 new_coord = wp->coord;
3583 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3585 marker_moveto ( t, x, y );
3592 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3594 tool_ed_t *t = data;
3595 VikViewport *vvp = t->vvp;
3597 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3600 if ( t->holding && event->button == 1 )
3603 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3606 if ( event->state & GDK_CONTROL_MASK )
3608 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3610 new_coord = tp->coord;
3614 if ( event->state & GDK_SHIFT_MASK )
3616 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3617 if ( wp && wp != vtl->current_wp )
3618 new_coord = wp->coord;
3621 marker_end_move ( t );
3623 vtl->current_wp->coord = new_coord;
3624 vik_layer_emit_update ( VIK_LAYER(vtl) );
3627 /* PUT IN RIGHT PLACE!!! */
3628 if ( event->button == 3 && vtl->waypoint_rightclick )
3630 if ( vtl->wp_right_click_menu )
3631 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
3632 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
3633 vik_trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_name, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
3634 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
3635 vtl->waypoint_rightclick = FALSE;
3640 /**** Begin track ***/
3641 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
3646 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3648 vtl->current_track = NULL;
3649 return tool_new_track_click ( vtl, event, vvp );
3652 /*** New track ****/
3654 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
3663 } new_track_move_passalong_t;
3665 /* sync and undraw, but only when we have time */
3666 static gboolean ct_sync ( gpointer passalong )
3668 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
3669 vik_viewport_sync ( p->vvp );
3670 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
3671 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
3672 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
3673 p->vtl->ct_sync_done = TRUE;
3678 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
3680 /* if we haven't sync'ed yet, we don't have time to do more. */
3681 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
3682 GList *iter = vtl->current_track->trackpoints;
3683 new_track_move_passalong_t *passalong;
3686 while ( iter->next )
3688 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
3689 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
3690 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
3691 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
3693 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
3694 passalong->vtl = vtl;
3695 passalong->vvp = vvp;
3698 passalong->x2 = event->x;
3699 passalong->y2 = event->y;
3701 /* this will sync and undraw when we have time to */
3702 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
3703 vtl->ct_sync_done = FALSE;
3704 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
3706 return VIK_LAYER_TOOL_ACK;
3709 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
3711 if ( vtl->current_track && event->keyval == GDK_Escape ) {
3712 vtl->current_track = NULL;
3713 vik_layer_emit_update ( VIK_LAYER(vtl) );
3715 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
3717 if ( vtl->current_track->trackpoints )
3719 GList *last = g_list_last(vtl->current_track->trackpoints);
3720 g_free ( last->data );
3721 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3723 vik_layer_emit_update ( VIK_LAYER(vtl) );
3729 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3733 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3736 if ( event->button == 3 && vtl->current_track )
3739 if ( vtl->current_track->trackpoints )
3741 GList *last = g_list_last(vtl->current_track->trackpoints);
3742 g_free ( last->data );
3743 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3745 vik_layer_emit_update ( VIK_LAYER(vtl) );
3749 if ( event->type == GDK_2BUTTON_PRESS )
3751 /* subtract last (duplicate from double click) tp then end */
3752 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
3754 GList *last = g_list_last(vtl->current_track->trackpoints);
3755 g_free ( last->data );
3756 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3757 /* undo last, then end */
3758 vtl->current_track = NULL;
3760 vik_layer_emit_update ( VIK_LAYER(vtl) );
3764 if ( ! vtl->current_track )
3766 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
3767 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
3769 vtl->current_track = vik_track_new();
3770 vtl->current_track->visible = TRUE;
3771 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3773 /* incase it was created by begin track */
3774 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3779 tp = vik_trackpoint_new();
3780 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
3782 /* snap to other TP */
3783 if ( event->state & GDK_CONTROL_MASK )
3785 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3787 tp->coord = other_tp->coord;
3790 tp->newsegment = FALSE;
3791 tp->has_timestamp = FALSE;
3793 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
3795 vtl->ct_x1 = vtl->ct_x2;
3796 vtl->ct_y1 = vtl->ct_y2;
3797 vtl->ct_x2 = event->x;
3798 vtl->ct_y2 = event->y;
3800 vik_layer_emit_update ( VIK_LAYER(vtl) );
3805 /*** New waypoint ****/
3807 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3812 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3815 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3817 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
3818 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
3819 vik_layer_emit_update ( VIK_LAYER(vtl) );
3824 /*** Edit trackpoint ****/
3826 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
3828 tool_ed_t *t = g_new(tool_ed_t, 1);
3834 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3836 tool_ed_t *t = data;
3837 VikViewport *vvp = t->vvp;
3838 TPSearchParams params;
3839 /* OUTDATED DOCUMENTATION:
3840 find 5 pixel range on each side. then put these UTM, and a pointer
3841 to the winning track name (and maybe the winning track itself), and a
3842 pointer to the winning trackpoint, inside an array or struct. pass
3843 this along, do a foreach on the tracks which will do a foreach on the
3846 params.x = event->x;
3847 params.y = event->y;
3848 params.closest_track_name = NULL;
3849 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3850 params.closest_tp = NULL;
3852 if ( event->button != 1 )
3855 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3858 if ( !vtl->vl.visible || !vtl->tracks_visible )
3861 if ( vtl->current_tpl )
3863 /* 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.) */
3864 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
3865 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
3867 g_assert ( current_tr );
3869 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
3871 if ( current_tr->visible &&
3872 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
3873 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
3874 marker_begin_move ( t, event->x, event->y );
3878 vtl->last_tpl = vtl->current_tpl;
3879 vtl->last_tp_track_name = vtl->current_tp_track_name;
3882 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3884 if ( params.closest_tp )
3886 vtl->current_tpl = params.closest_tpl;
3887 vtl->current_tp_track_name = params.closest_track_name;
3888 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
3889 trw_layer_tpwin_init ( vtl );
3890 vik_layer_emit_update ( VIK_LAYER(vtl) );
3894 /* these aren't the droids you're looking for */
3898 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
3900 tool_ed_t *t = data;
3901 VikViewport *vvp = t->vvp;
3903 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3909 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3912 if ( event->state & GDK_CONTROL_MASK )
3914 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3915 if ( tp && tp != vtl->current_tpl->data )
3916 new_coord = tp->coord;
3918 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3921 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3922 marker_moveto ( t, x, y );
3930 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3932 tool_ed_t *t = data;
3933 VikViewport *vvp = t->vvp;
3935 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3937 if ( event->button != 1)
3942 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3945 if ( event->state & GDK_CONTROL_MASK )
3947 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3948 if ( tp && tp != vtl->current_tpl->data )
3949 new_coord = tp->coord;
3952 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3954 marker_end_move ( t );
3956 /* diff dist is diff from orig */
3957 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3958 /* can't join with itself! */
3959 trw_layer_cancel_last_tp ( vtl );
3961 vik_layer_emit_update ( VIK_LAYER(vtl) );
3968 /*** Magic Scissors ***/
3969 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
3974 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3977 if ( !vtl ) return FALSE;
3978 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
3979 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
3981 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
3983 vtl->magic_scissors_coord = *new_end;
3985 vik_layer_emit_update ( VIK_LAYER(vtl) );
3986 /* remove last ' to:...' */
3987 if ( vtl->magic_scissors_current_track->comment ) {
3988 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
3989 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
3990 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
3991 last_to - vtl->magic_scissors_current_track->comment - 1);
3992 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
3997 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
3998 struct LatLon start, end;
3999 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
4000 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
4003 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
4004 vik_coord_to_latlon ( &(tmp), &end );
4005 vtl->magic_scissors_coord = tmp; /* for continuations */
4007 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
4008 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
4009 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
4011 vtl->magic_scissors_check_added_track = TRUE;
4012 vtl->magic_scissors_started = FALSE;
4015 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
4016 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
4017 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
4018 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
4019 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
4020 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
4023 /* see if anything was done -- a track was added or appended to */
4024 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
4027 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
4030 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
4032 vtl->magic_scissors_current_track = tr;
4034 g_free ( vtl->magic_scissors_added_track_name );
4035 vtl->magic_scissors_added_track_name = NULL;
4036 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
4037 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
4038 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
4039 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4041 vtl->magic_scissors_check_added_track = FALSE;
4042 vtl->magic_scissors_append = FALSE;
4044 vik_layer_emit_update ( VIK_LAYER(vtl) );
4046 vtl->magic_scissors_started = TRUE;
4047 vtl->magic_scissors_coord = tmp;
4048 vtl->magic_scissors_current_track = NULL;
4053 /*** Show picture ****/
4055 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
4060 /* Params are: vvp, event, last match found or NULL */
4061 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
4063 if ( wp->image && wp->visible )
4065 gint x, y, slackx, slacky;
4066 GdkEventButton *event = (GdkEventButton *) params[1];
4068 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
4069 slackx = wp->image_width / 2;
4070 slacky = wp->image_height / 2;
4071 if ( x <= event->x + slackx && x >= event->x - slackx
4072 && y <= event->y + slacky && y >= event->y - slacky )
4074 params[2] = wp->image; /* we've found a match. however continue searching
4075 * since we want to find the last match -- that
4076 * is, the match that was drawn last. */
4081 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4083 gpointer params[3] = { vvp, event, NULL };
4084 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4086 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
4089 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
4091 ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
4094 gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
4095 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
4096 g_free ( quoted_file );
4097 if ( ! g_spawn_command_line_async ( cmd, &err ) )
4099 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch eog to open file.") );
4100 g_error_free ( err );
4103 #endif /* WINDOWS */
4104 return TRUE; /* found a match */
4107 return FALSE; /* go through other layers, searching for a match */
4110 /***************************************************************************
4112 ***************************************************************************/
4118 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
4120 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
4121 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
4124 static void create_thumbnails_thread ( GSList *pics, gpointer threaddata )
4126 guint total = g_slist_length(pics), done = 0;
4129 a_thumbnails_create ( (gchar *) pics->data );
4130 a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
4135 static void free_pics_slist ( GSList *pics )
4139 g_free ( pics->data );
4140 pics = g_slist_delete_link ( pics, pics );
4144 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
4146 if ( ! vtl->has_verified_thumbnails )
4148 GSList *pics = NULL;
4149 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
4152 gint len = g_slist_length ( pics );
4153 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
4154 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl), tmp, (vik_thr_func) create_thumbnails_thread, pics, (vik_thr_free_func) free_pics_slist, NULL, len );
4160 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
4162 return vtl->coord_mode;
4167 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
4169 vik_coord_convert ( &(wp->coord), *dest_mode );
4172 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
4174 vik_track_convert ( tr, *dest_mode );
4177 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
4179 if ( vtl->coord_mode != dest_mode )
4181 vtl->coord_mode = dest_mode;
4182 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
4183 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
4187 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
4189 return g_hash_table_lookup ( vtl->waypoints, name );
4192 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
4194 return g_hash_table_lookup ( vtl->tracks, name );
4197 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
4199 vtl->menu_selection = selection;
4202 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
4204 return(vtl->menu_selection);
4207 /* ----------- Downloading maps along tracks --------------- */
4209 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
4211 /* TODO: calculating based on current size of viewport */
4212 const gdouble w_at_zoom_0_125 = 0.0013;
4213 const gdouble h_at_zoom_0_125 = 0.0011;
4214 gdouble zoom_factor = zoom_level/0.125;
4216 wh->lat = h_at_zoom_0_125 * zoom_factor;
4217 wh->lon = w_at_zoom_0_125 * zoom_factor;
4219 return 0; /* all OK */
4222 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
4224 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
4225 (dist->lat >= ABS(to->north_south - from->north_south)))
4228 VikCoord *coord = g_malloc(sizeof(VikCoord));
4229 coord->mode = VIK_COORD_LATLON;
4231 if (ABS(gradient) < 1) {
4232 if (from->east_west > to->east_west)
4233 coord->east_west = from->east_west - dist->lon;
4235 coord->east_west = from->east_west + dist->lon;
4236 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
4238 if (from->north_south > to->north_south)
4239 coord->north_south = from->north_south - dist->lat;
4241 coord->north_south = from->north_south + dist->lat;
4242 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
4248 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
4250 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
4251 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
4253 VikCoord *next = from;
4255 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
4257 list = g_list_prepend(list, next);
4263 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
4265 typedef struct _Rect {
4270 #define GLRECT(iter) ((Rect *)((iter)->data))
4273 GList *rects_to_download = NULL;
4276 if (get_download_area_width(vvp, zoom_level, &wh))
4279 GList *iter = tr->trackpoints;
4283 gboolean new_map = TRUE;
4284 VikCoord *cur_coord, tl, br;
4287 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
4289 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4290 rect = g_malloc(sizeof(Rect));
4293 rect->center = *cur_coord;
4294 rects_to_download = g_list_prepend(rects_to_download, rect);
4299 gboolean found = FALSE;
4300 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4301 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
4312 GList *fillins = NULL;
4313 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
4314 /* seems that ATM the function get_next_coord works only for LATLON */
4315 if ( cur_coord->mode == VIK_COORD_LATLON ) {
4316 /* fill-ins for far apart points */
4317 GList *cur_rect, *next_rect;
4318 for (cur_rect = rects_to_download;
4319 (next_rect = cur_rect->next) != NULL;
4320 cur_rect = cur_rect->next) {
4321 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
4322 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
4323 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
4327 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
4330 GList *iter = fillins;
4332 cur_coord = (VikCoord *)(iter->data);
4333 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4334 rect = g_malloc(sizeof(Rect));
4337 rect->center = *cur_coord;
4338 rects_to_download = g_list_prepend(rects_to_download, rect);
4343 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4344 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
4348 for (iter = fillins; iter; iter = iter->next)
4350 g_list_free(fillins);
4352 if (rects_to_download) {
4353 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
4354 g_free(rect_iter->data);
4355 g_list_free(rects_to_download);
4359 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6])
4362 gint selected_map, default_map;
4363 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
4364 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
4365 gint selected_zoom, default_zoom;
4369 VikTrwLayer *vtl = pass_along[0];
4370 VikLayersPanel *vlp = pass_along[1];
4371 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4372 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
4374 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS);
4375 int num_maps = g_list_length(vmls);
4378 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
4382 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
4383 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
4385 gchar **np = map_names;
4386 VikMapsLayer **lp = map_layers;
4387 for (i = 0; i < num_maps; i++) {
4388 gboolean dup = FALSE;
4389 vml = (VikMapsLayer *)(vmls->data);
4390 for (j = 0; j < i; j++) { /* no duplicate allowed */
4391 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
4398 *np++ = vik_maps_layer_get_map_label(vml);
4404 num_maps = lp - map_layers;
4406 for (default_map = 0; default_map < num_maps; default_map++) {
4407 /* TODO: check for parent layer's visibility */
4408 if (VIK_LAYER(map_layers[default_map])->visible)
4411 default_map = (default_map == num_maps) ? 0 : default_map;
4413 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
4414 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
4415 if (cur_zoom == zoom_vals[default_zoom])
4418 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
4420 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
4423 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
4426 for (i = 0; i < num_maps; i++)
4427 g_free(map_names[i]);
4435 /**** lowest waypoint number calculation ***/
4436 static gint highest_wp_number_name_to_number(const gchar *name) {
4437 if ( strlen(name) == 3 ) {
4439 if ( n < 100 && name[0] != '0' )
4441 if ( n < 10 && name[0] != '0' )
4449 static void highest_wp_number_reset(VikTrwLayer *vtl)
4451 vtl->highest_wp_number = -1;
4454 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
4456 /* if is bigger that top, add it */
4457 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
4458 if ( new_wp_num > vtl->highest_wp_number )
4459 vtl->highest_wp_number = new_wp_num;
4462 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
4464 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
4465 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
4466 if ( vtl->highest_wp_number == old_wp_num ) {
4468 vtl->highest_wp_number --;
4470 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4471 /* search down until we find something that *does* exist */
4473 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
4474 vtl->highest_wp_number --;
4475 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4480 /* get lowest unused number */
4481 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
4484 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
4486 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
4487 return g_strdup(buf);