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,
445 (VikLayerFuncMarshall) trw_layer_marshall,
446 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
448 (VikLayerFuncSetParam) trw_layer_set_param,
449 (VikLayerFuncGetParam) trw_layer_get_param,
451 (VikLayerFuncReadFileData) a_gpspoint_read_file,
452 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
454 (VikLayerFuncDeleteItem) trw_layer_del_item,
455 (VikLayerFuncCopyItem) trw_layer_copy_item,
456 (VikLayerFuncPasteItem) trw_layer_paste_item,
457 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
459 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
462 /* for copy & paste (I think?) */
470 GType vik_trw_layer_get_type ()
472 static GType vtl_type = 0;
476 static const GTypeInfo vtl_info =
478 sizeof (VikTrwLayerClass),
479 NULL, /* base_init */
480 NULL, /* base_finalize */
481 NULL, /* class init */
482 NULL, /* class_finalize */
483 NULL, /* class_data */
484 sizeof (VikTrwLayer),
486 NULL /* instance init */
488 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
494 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
496 static gpointer pass_along[5];
502 pass_along[1] = NULL;
503 pass_along[2] = GINT_TO_POINTER (subtype);
504 pass_along[3] = sublayer;
505 pass_along[4] = NULL;
507 trw_layer_delete_item ( pass_along );
510 static void trw_layer_copy_item_cb( gpointer pass_along[5])
512 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
513 gint subtype = GPOINTER_TO_INT (pass_along[2]);
514 gpointer * sublayer = pass_along[3];
518 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
521 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
526 static void trw_layer_cut_item_cb( gpointer pass_along[5])
528 trw_layer_copy_item_cb(pass_along);
529 trw_layer_delete_item(pass_along);
532 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
543 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
545 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
547 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
550 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
551 fi = g_malloc ( *len );
552 fi->len = strlen(sublayer) + 1;
553 memcpy(fi->data, sublayer, fi->len);
554 memcpy(fi->data + fi->len, id, il);
556 *item = (guint8 *)fi;
559 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
561 FlatItem *fi = (FlatItem *) item;
563 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
568 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
569 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
570 vik_trw_layer_add_waypoint ( vtl, name, w );
571 waypoint_convert(name, w, &vtl->coord_mode);
572 // Consider if redraw necessary for the new item
573 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
574 vik_layer_emit_update ( VIK_LAYER(vtl) );
577 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
581 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
582 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
583 vik_trw_layer_add_track ( vtl, name, t );
584 track_convert(name, t, &vtl->coord_mode);
585 // Consider if redraw necessary for the new item
586 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
587 vik_layer_emit_update ( VIK_LAYER(vtl) );
593 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
600 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
604 case PARAM_TV: vtl->tracks_visible = data.b; break;
605 case PARAM_WV: vtl->waypoints_visible = data.b; break;
606 case PARAM_DM: vtl->drawmode = data.u; break;
607 case PARAM_DP: vtl->drawpoints = data.b; break;
608 case PARAM_DE: vtl->drawelevation = data.b; break;
609 case PARAM_DS: vtl->drawstops = data.b; break;
610 case PARAM_DL: vtl->drawlines = data.b; break;
611 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
612 vtl->stop_length = data.u;
614 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
615 vtl->elevation_factor = data.u;
617 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
619 vtl->line_thickness = data.u;
620 trw_layer_new_track_gcs ( vtl, vp );
623 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
625 vtl->bg_line_thickness = data.u;
626 trw_layer_new_track_gcs ( vtl, vp );
631 /* Convert to store internally
632 NB file operation always in internal units (metres per second) */
633 vik_units_speed_t speed_units = a_vik_get_units_speed ();
634 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
635 vtl->velocity_min = data.d;
636 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
637 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
638 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
639 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
642 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
647 /* Convert to store internally
648 NB file operation always in internal units (metres per second) */
649 vik_units_speed_t speed_units = a_vik_get_units_speed ();
650 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
651 vtl->velocity_max = data.d;
652 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
653 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
654 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
655 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
658 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
661 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
662 case PARAM_DLA: vtl->drawlabels = data.b; break;
663 case PARAM_DI: vtl->drawimages = data.b; break;
664 case PARAM_IS: if ( data.u != vtl->image_size )
666 vtl->image_size = data.u;
667 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
668 g_queue_free ( vtl->image_cache );
669 vtl->image_cache = g_queue_new ();
672 case PARAM_IA: vtl->image_alpha = data.u; break;
673 case PARAM_ICS: vtl->image_cache_size = data.u;
674 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
675 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
677 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
678 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
679 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
680 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
681 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
682 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
683 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
688 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
690 VikLayerParamData rv;
693 case PARAM_TV: rv.b = vtl->tracks_visible; break;
694 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
695 case PARAM_DM: rv.u = vtl->drawmode; break;
696 case PARAM_DP: rv.b = vtl->drawpoints; break;
697 case PARAM_DE: rv.b = vtl->drawelevation; break;
698 case PARAM_EF: rv.u = vtl->elevation_factor; break;
699 case PARAM_DS: rv.b = vtl->drawstops; break;
700 case PARAM_SL: rv.u = vtl->stop_length; break;
701 case PARAM_DL: rv.b = vtl->drawlines; break;
702 case PARAM_LT: rv.u = vtl->line_thickness; break;
703 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
706 /* Convert to store internally
707 NB file operation always in internal units (metres per second) */
708 vik_units_speed_t speed_units = a_vik_get_units_speed ();
709 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
710 rv.d = vtl->velocity_min;
711 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
712 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
713 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
714 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
717 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
722 /* Convert to store internally
723 NB file operation always in internal units (metres per second) */
724 vik_units_speed_t speed_units = a_vik_get_units_speed ();
725 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
726 rv.d = vtl->velocity_max;
727 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
728 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
729 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
730 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
733 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
736 case PARAM_DLA: rv.b = vtl->drawlabels; break;
737 case PARAM_DI: rv.b = vtl->drawimages; break;
738 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
739 case PARAM_IS: rv.u = vtl->image_size; break;
740 case PARAM_IA: rv.u = vtl->image_alpha; break;
741 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
742 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
743 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
744 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
745 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
746 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
747 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
748 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
753 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
764 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
765 a_gpx_write_file(vtl, f);
766 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
769 g_file_get_contents(tmpname, &dd, &dl, NULL);
770 *len = sizeof(pl) + pl + dl;
771 *data = g_malloc(*len);
772 memcpy(*data, &pl, sizeof(pl));
773 memcpy(*data + sizeof(pl), pd, pl);
774 memcpy(*data + sizeof(pl) + pl, dd, dl);
783 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
785 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
791 memcpy(&pl, data, sizeof(pl));
793 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
796 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
797 g_critical("couldn't open temp file");
800 fwrite(data, len - pl - sizeof(pl), 1, f);
802 a_gpx_read_file(rv, f);
810 static GList * str_array_to_glist(gchar* data[])
814 for (p = (gpointer)data; *p; p++)
815 gl = g_list_prepend(gl, *p);
816 return(g_list_reverse(gl));
819 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
821 return (strcasecmp(s1, s2) == 0);
824 static guint strcase_hash(gconstpointer v)
826 /* 31 bit hash function */
829 gchar s[128]; /* malloc is too slow for reading big files */
832 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
833 p[i] = toupper(t[i]);
839 for (p += 1; *p != '\0'; p++)
840 h = (h << 5) - h + *p;
846 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
848 if (trw_layer_params[PARAM_DM].widget_data == NULL)
849 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
850 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
851 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
853 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
854 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
856 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
857 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
858 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
859 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
861 /* TODO: constants at top */
862 rv->waypoints_visible = rv->tracks_visible = TRUE;
863 rv->drawmode = drawmode;
864 rv->drawpoints = TRUE;
865 rv->drawstops = FALSE;
866 rv->drawelevation = FALSE;
867 rv->elevation_factor = 30;
868 rv->stop_length = 60;
869 rv->drawlines = TRUE;
870 rv->wplabellayout = NULL;
871 rv->wp_right_click_menu = NULL;
872 rv->waypoint_gc = NULL;
873 rv->waypoint_text_gc = NULL;
874 rv->waypoint_bg_gc = NULL;
876 rv->velocity_max = 5.0;
877 rv->velocity_min = 0.0;
878 rv->line_thickness = 1;
879 rv->bg_line_thickness = 0;
880 rv->current_wp = NULL;
881 rv->current_wp_name = NULL;
882 rv->current_track = NULL;
883 rv->current_tpl = NULL;
884 rv->current_tp_track_name = NULL;
885 rv->moving_tp = FALSE;
886 rv->moving_wp = FALSE;
888 rv->ct_sync_done = TRUE;
890 rv->magic_scissors_started = FALSE;
891 rv->magic_scissors_check_added_track = FALSE;
892 rv->magic_scissors_added_track_name = NULL;
893 rv->magic_scissors_current_track = NULL;
894 rv->magic_scissors_append = FALSE;
896 rv->waypoint_rightclick = FALSE;
898 rv->last_tp_track_name = NULL;
900 rv->image_cache = g_queue_new();
902 rv->image_alpha = 255;
903 rv->image_cache_size = 300;
904 rv->drawimages = TRUE;
905 rv->drawlabels = TRUE;
910 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
912 g_hash_table_destroy(trwlayer->waypoints);
913 g_hash_table_destroy(trwlayer->tracks);
915 /* ODC: replace with GArray */
916 trw_layer_free_track_gcs ( trwlayer );
918 if ( trwlayer->wp_right_click_menu )
919 gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) );
921 if ( trwlayer->wplabellayout != NULL)
922 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
924 if ( trwlayer->waypoint_gc != NULL )
925 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
927 if ( trwlayer->waypoint_text_gc != NULL )
928 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
930 if ( trwlayer->waypoint_bg_gc != NULL )
931 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
933 if ( trwlayer->waypoint_font != NULL )
934 gdk_font_unref ( trwlayer->waypoint_font );
936 if ( trwlayer->tpwin != NULL )
937 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
939 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
940 g_queue_free ( trwlayer->image_cache );
943 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
946 dp->xmpp = vik_viewport_get_xmpp ( vp );
947 dp->ympp = vik_viewport_get_ympp ( vp );
948 dp->width = vik_viewport_get_width ( vp );
949 dp->height = vik_viewport_get_height ( vp );
950 dp->center = vik_viewport_get_center ( vp );
951 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
952 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
957 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
958 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
959 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
961 dp->ce1 = dp->center->east_west-w2;
962 dp->ce2 = dp->center->east_west+w2;
963 dp->cn1 = dp->center->north_south-h2;
964 dp->cn2 = dp->center->north_south+h2;
965 } else if ( dp->lat_lon ) {
966 VikCoord upperleft, bottomright;
967 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
968 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
969 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
970 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
971 dp->ce1 = upperleft.east_west;
972 dp->ce2 = bottomright.east_west;
973 dp->cn1 = bottomright.north_south;
974 dp->cn2 = upperleft.north_south;
977 dp->track_gc_iter = 0;
980 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
982 static gdouble rv = 0;
983 if ( tp1->has_timestamp && tp2->has_timestamp )
985 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
986 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
989 return VIK_TRW_LAYER_TRACK_GC_MIN;
990 else if ( vtl->velocity_min >= vtl->velocity_max )
991 return VIK_TRW_LAYER_TRACK_GC_MAX;
993 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
995 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
996 return VIK_TRW_LAYER_TRACK_GC_MAX;
1000 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1003 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1005 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1006 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1007 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1008 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1011 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1013 /* TODO: this function is a mess, get rid of any redundancy */
1014 GList *list = track->trackpoints;
1016 gboolean useoldvals = TRUE;
1018 gboolean drawpoints;
1020 gboolean drawelevation;
1021 gdouble min_alt, max_alt, alt_diff = 0;
1023 const guint8 tp_size_reg = 2;
1024 const guint8 tp_size_cur = 4;
1027 if ( dp->vtl->drawelevation )
1029 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1030 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1031 alt_diff = max_alt - min_alt;
1034 if ( ! track->visible )
1037 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1038 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1039 trw_layer_draw_track ( name, track, dp, TRUE );
1041 if ( drawing_white_background )
1042 drawpoints = drawstops = FALSE;
1044 drawpoints = dp->vtl->drawpoints;
1045 drawstops = dp->vtl->drawstops;
1048 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1049 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1051 if ( track == dp->vtl->current_track )
1052 main_gc = dp->vtl->current_track_gc;
1054 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1057 int x, y, oldx, oldy;
1058 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1060 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1062 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1064 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1066 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1067 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1073 while ((list = g_list_next(list)))
1075 tp = VIK_TRACKPOINT(list->data);
1076 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1078 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1079 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1080 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1081 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1082 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1084 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1086 if ( drawpoints && ! drawing_white_background )
1089 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1092 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1093 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 );
1096 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 );
1099 if ((!tp->newsegment) && (dp->vtl->drawlines))
1101 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1103 /* UTM only: zone check */
1104 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1105 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1107 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1108 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1109 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1113 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1115 if ( drawing_white_background ) {
1116 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1120 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1121 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1123 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1124 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1128 tmp[1].y = oldy-FIXALTITUDE(list->data);
1130 tmp[2].y = y-FIXALTITUDE(list->next->data);
1135 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1136 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1138 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1139 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1141 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1151 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1153 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1154 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1156 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1157 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1158 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1159 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1162 if ( drawing_white_background )
1163 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1165 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1169 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1170 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1177 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1178 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1179 dp->track_gc_iter = 0;
1182 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1183 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1185 trw_layer_draw_track ( name, track, dp, FALSE );
1188 static void cached_pixbuf_free ( CachedPixbuf *cp )
1190 g_object_unref ( G_OBJECT(cp->pixbuf) );
1191 g_free ( cp->image );
1194 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1196 return strcmp ( cp->image, name );
1199 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1202 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1203 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1204 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1207 GdkPixbuf *sym = NULL;
1208 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1210 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1212 if ( wp->image && dp->vtl->drawimages )
1214 GdkPixbuf *pixbuf = NULL;
1217 if ( dp->vtl->image_alpha == 0)
1220 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1222 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1225 gchar *image = wp->image;
1226 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1227 if ( ! regularthumb )
1229 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1230 image = "\x12\x00"; /* this shouldn't occur naturally. */
1234 CachedPixbuf *cp = NULL;
1235 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1236 if ( dp->vtl->image_size == 128 )
1237 cp->pixbuf = regularthumb;
1240 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1241 g_assert ( cp->pixbuf );
1242 g_object_unref ( G_OBJECT(regularthumb) );
1244 cp->image = g_strdup ( image );
1246 /* needed so 'click picture' tool knows how big the pic is; we don't
1247 * store it in cp because they may have been freed already. */
1248 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1249 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1251 g_queue_push_head ( dp->vtl->image_cache, cp );
1252 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1253 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1255 pixbuf = cp->pixbuf;
1259 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1265 w = gdk_pixbuf_get_width ( pixbuf );
1266 h = gdk_pixbuf_get_height ( pixbuf );
1268 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1270 if ( dp->vtl->image_alpha == 255 )
1271 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1273 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1275 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1279 /* DRAW ACTUAL DOT */
1280 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1281 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 );
1283 else if ( wp == dp->vtl->current_wp ) {
1284 switch ( dp->vtl->wp_symbol ) {
1285 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;
1286 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;
1287 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;
1288 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 );
1289 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 );
1293 switch ( dp->vtl->wp_symbol ) {
1294 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;
1295 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;
1296 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;
1297 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 );
1298 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;
1302 if ( dp->vtl->drawlabels )
1304 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1305 gint label_x, label_y;
1307 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1308 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1309 label_x = x - width/2;
1311 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1313 label_y = y - dp->vtl->wp_size - height - 2;
1315 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1316 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1321 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1323 static struct DrawingParams dp;
1324 g_assert ( l != NULL );
1326 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1329 if ( l->tracks_visible )
1330 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1332 if (l->waypoints_visible)
1333 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1336 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1339 if ( vtl->track_bg_gc )
1341 g_object_unref ( vtl->track_bg_gc );
1342 vtl->track_bg_gc = NULL;
1344 if ( vtl->current_track_gc )
1346 g_object_unref ( vtl->current_track_gc );
1347 vtl->current_track_gc = NULL;
1350 if ( ! vtl->track_gc )
1352 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1353 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1354 g_array_free ( vtl->track_gc, TRUE );
1355 vtl->track_gc = NULL;
1358 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1360 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1361 gint width = vtl->line_thickness;
1363 if ( vtl->track_gc )
1364 trw_layer_free_track_gcs ( vtl );
1366 if ( vtl->track_bg_gc )
1367 g_object_unref ( vtl->track_bg_gc );
1368 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1370 if ( vtl->current_track_gc )
1371 g_object_unref ( vtl->current_track_gc );
1372 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1373 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1375 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1377 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1379 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1380 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1381 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1382 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1383 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1384 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1385 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1386 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1387 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1388 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1390 gc[11] = vik_viewport_new_gc ( vp, "#ff0000", width ); /* above range */
1392 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1394 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1397 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1399 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1400 PangoFontDescription *pfd;
1401 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1402 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1403 pango_layout_set_font_description (rv->wplabellayout, pfd);
1404 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1405 pango_font_description_free (pfd);
1407 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1409 trw_layer_new_track_gcs ( rv, vp );
1411 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1412 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1413 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1414 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1416 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1418 rv->has_verified_thumbnails = FALSE;
1419 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1421 rv->wp_draw_symbols = TRUE;
1423 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1425 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1430 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] )
1432 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1434 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1435 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 );
1437 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 );
1440 *new_iter = *((GtkTreeIter *) pass_along[1]);
1441 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1443 if ( ! track->visible )
1444 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1447 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] )
1449 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1450 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1451 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 );
1453 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 );
1456 *new_iter = *((GtkTreeIter *) pass_along[1]);
1457 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1459 if ( ! wp->visible )
1460 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1464 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1467 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1469 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1470 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1472 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1474 if ( ! vtl->tracks_visible )
1475 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1477 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1479 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1480 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1482 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1485 if ( ! vtl->waypoints_visible )
1486 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1488 pass_along[0] = &(vtl->waypoints_iter);
1489 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1491 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1495 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1499 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1500 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1501 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1503 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1505 return (t->visible ^= 1);
1509 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1511 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1513 return (t->visible ^= 1);
1521 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1526 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1528 return l->waypoints;
1531 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1533 static VikCoord fixme;
1534 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1535 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1536 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1537 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1538 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1539 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1540 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1541 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1542 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1545 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1548 static VikCoord fixme;
1552 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1553 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1554 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1555 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1556 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1557 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1558 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1559 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1560 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1565 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1567 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1568 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1570 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1571 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1572 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1573 maxmin[0].lat = wpt_maxmin[0].lat;
1576 maxmin[0].lat = trk_maxmin[0].lat;
1578 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1579 maxmin[0].lon = wpt_maxmin[0].lon;
1582 maxmin[0].lon = trk_maxmin[0].lon;
1584 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1585 maxmin[1].lat = wpt_maxmin[1].lat;
1588 maxmin[1].lat = trk_maxmin[1].lat;
1590 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1591 maxmin[1].lon = wpt_maxmin[1].lon;
1594 maxmin[1].lon = trk_maxmin[1].lon;
1598 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1600 /* 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... */
1601 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1602 trw_layer_find_maxmin (vtl, maxmin);
1603 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1607 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1608 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1613 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1616 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1617 goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord );
1619 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1622 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1624 /* First set the center [in case previously viewing from elsewhere] */
1625 /* Then loop through zoom levels until provided positions are in view */
1626 /* This method is not particularly fast - but should work well enough */
1627 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1629 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1630 vik_viewport_set_center_coord ( vvp, &coord );
1632 /* Convert into definite 'smallest' and 'largest' positions */
1633 struct LatLon minmin;
1634 if ( maxmin[0].lat < maxmin[1].lat )
1635 minmin.lat = maxmin[0].lat;
1637 minmin.lat = maxmin[1].lat;
1639 struct LatLon maxmax;
1640 if ( maxmin[0].lon > maxmin[1].lon )
1641 maxmax.lon = maxmin[0].lon;
1643 maxmax.lon = maxmin[1].lon;
1645 /* Never zoom in too far - generally not that useful, as too close ! */
1646 /* Always recalculate the 'best' zoom level */
1648 vik_viewport_set_zoom ( vvp, zoom );
1650 gdouble min_lat, max_lat, min_lon, max_lon;
1651 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1652 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1653 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1654 /* NB I think the logic used in this test to determine if the bounds is within view
1655 fails if track goes across 180 degrees longitude.
1656 Hopefully that situation is not too common...
1657 Mind you viking doesn't really do edge locations to well anyway */
1658 if ( min_lat < minmin.lat &&
1659 max_lat > minmin.lat &&
1660 min_lon < maxmax.lon &&
1661 max_lon > maxmax.lon )
1662 /* Found within zoom level */
1667 vik_viewport_set_zoom ( vvp, zoom );
1671 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
1673 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
1674 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1675 trw_layer_find_maxmin (vtl, maxmin);
1676 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1679 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
1684 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
1686 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])) ) ) {
1687 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1690 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1693 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
1695 GtkWidget *file_selector;
1697 gboolean failed = FALSE;
1698 file_selector = gtk_file_chooser_dialog_new (title,
1700 GTK_FILE_CHOOSER_ACTION_SAVE,
1701 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1702 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1704 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
1706 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
1708 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
1709 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
1711 gtk_widget_hide ( file_selector );
1712 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
1717 if ( a_dialog_overwrite ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
1719 gtk_widget_hide ( file_selector );
1720 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
1725 gtk_widget_destroy ( file_selector );
1727 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
1730 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
1732 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
1735 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
1737 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
1740 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
1742 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX );
1745 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
1747 gpointer layer_and_vlp[2];
1748 layer_and_vlp[0] = pass_along[0];
1749 layer_and_vlp[1] = pass_along[1];
1751 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
1752 gchar *auto_save_name = g_strdup ( pass_along[3] );
1753 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
1754 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
1756 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
1758 g_free ( auto_save_name );
1761 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
1763 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
1764 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
1765 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
1766 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1768 GTK_RESPONSE_REJECT,
1770 GTK_RESPONSE_ACCEPT,
1773 GtkWidget *label, *entry;
1774 label = gtk_label_new(_("Waypoint Name:"));
1775 entry = gtk_entry_new();
1777 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
1778 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
1779 gtk_widget_show_all ( label );
1780 gtk_widget_show_all ( entry );
1782 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
1784 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
1787 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
1790 for ( i = strlen(upname)-1; i >= 0; i-- )
1791 upname[i] = toupper(upname[i]);
1793 wp = g_hash_table_lookup ( wps, upname );
1796 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
1799 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
1800 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1801 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 ) );
1808 gtk_widget_destroy ( dia );
1811 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
1813 gchar *name = highest_wp_number_get(vtl);
1814 VikWaypoint *wp = vik_waypoint_new();
1815 wp->coord = *def_coord;
1817 if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) )
1820 vik_trw_layer_add_waypoint ( vtl, name, wp );
1823 vik_waypoint_free(wp);
1827 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
1830 struct LatLon one_ll, two_ll;
1831 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1833 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1834 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1835 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
1836 VikViewport *vvp = vik_window_viewport(vw);
1837 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
1838 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
1839 vik_coord_to_latlon(&one, &one_ll);
1840 vik_coord_to_latlon(&two, &two_ll);
1841 if (one_ll.lat > two_ll.lat) {
1842 maxmin[0].lat = one_ll.lat;
1843 maxmin[1].lat = two_ll.lat;
1846 maxmin[0].lat = two_ll.lat;
1847 maxmin[1].lat = one_ll.lat;
1849 if (one_ll.lon > two_ll.lon) {
1850 maxmin[0].lon = one_ll.lon;
1851 maxmin[1].lon = two_ll.lon;
1854 maxmin[0].lon = two_ll.lon;
1855 maxmin[1].lon = one_ll.lon;
1857 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
1860 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
1862 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1863 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1864 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1866 trw_layer_find_maxmin (vtl, maxmin);
1867 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
1870 static void trw_layer_new_wp ( gpointer lav[2] )
1872 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1873 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1874 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
1875 instead return true if you want to update. */
1876 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 )
1877 vik_layers_panel_emit_update ( vlp );
1880 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
1882 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1883 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1885 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1886 struct LatLon maxmin[2] = { {0,0}, {0,0} };
1887 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
1888 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
1889 vik_layers_panel_emit_update ( vlp );
1893 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
1895 /* NB do not care if wp is visible or not */
1896 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
1899 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
1901 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1902 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1904 /* Only 1 waypoint - jump straight to it */
1905 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
1906 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
1907 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
1909 /* If at least 2 waypoints - find center and then zoom to fit */
1910 else if ( g_hash_table_size (vtl->waypoints) > 1 )
1912 struct LatLon maxmin[2] = { {0,0}, {0,0} };
1913 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
1914 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
1917 vik_layers_panel_emit_update ( vlp );
1920 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
1922 static gpointer pass_along[2];
1924 GtkWidget *export_submenu;
1925 GtkWidget *wikipedia_submenu;
1926 pass_along[0] = vtl;
1927 pass_along[1] = vlp;
1929 item = gtk_menu_item_new();
1930 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1931 gtk_widget_show ( item );
1933 item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") );
1934 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
1935 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1936 gtk_widget_show ( item );
1938 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
1939 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
1940 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1941 gtk_widget_show ( item );
1943 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
1944 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
1945 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1946 gtk_widget_show ( item );
1948 item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
1949 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
1950 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1951 gtk_widget_show ( item );
1953 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint") );
1954 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
1955 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1956 gtk_widget_show ( item );
1958 export_submenu = gtk_menu_new ();
1959 item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
1960 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1961 gtk_widget_show ( item );
1962 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1964 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point") );
1965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
1966 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1967 gtk_widget_show ( item );
1969 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper") );
1970 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1971 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1972 gtk_widget_show ( item );
1974 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX") );
1975 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1976 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1977 gtk_widget_show ( item );
1979 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
1980 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
1981 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1982 gtk_widget_show ( item );
1984 #ifdef VIK_CONFIG_GEONAMES
1985 wikipedia_submenu = gtk_menu_new();
1986 item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
1987 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
1988 gtk_widget_show(item);
1989 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
1991 item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
1992 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
1993 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
1994 gtk_widget_show ( item );
1996 item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
1997 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
1998 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
1999 gtk_widget_show ( item );
2002 #ifdef VIK_CONFIG_OPENSTREETMAP
2003 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
2004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2005 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2006 gtk_widget_show ( item );
2009 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2010 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2012 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2013 gtk_widget_show ( item );
2016 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2017 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2019 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2020 gtk_widget_show ( item );
2024 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2026 if ( VIK_LAYER(vtl)->realized )
2028 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2030 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2033 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2034 // Visibility column always needed for waypoints
2035 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2036 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2038 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2040 // Actual setting of visibility dependent on the waypoint
2041 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2042 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2043 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2047 highest_wp_number_add_wp(vtl, name);
2048 g_hash_table_insert ( vtl->waypoints, name, wp );
2052 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2054 if ( VIK_LAYER(vtl)->realized )
2056 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2058 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2061 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2062 // Visibility column always needed for tracks
2063 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2064 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2066 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2068 // Actual setting of visibility dependent on the track
2069 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2070 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2071 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2075 g_hash_table_insert ( vtl->tracks, name, t );
2079 /* to be called whenever a track has been deleted or may have been changed. */
2080 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2082 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2083 trw_layer_cancel_current_tp ( vtl, FALSE );
2084 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2085 trw_layer_cancel_last_tp ( vtl );
2088 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2091 gchar *newname = g_strdup(name);
2092 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2093 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2094 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2096 newname = new_newname;
2102 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2104 vik_trw_layer_add_waypoint ( vtl,
2105 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2108 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2110 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
2111 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2112 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2113 vik_track_free ( tr );
2114 vtl->magic_scissors_append = FALSE; /* this means we have added it */
2116 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2117 vik_trw_layer_add_track ( vtl, new_name, tr );
2119 if ( vtl->magic_scissors_check_added_track ) {
2120 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2121 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2122 g_free ( vtl->magic_scissors_added_track_name );
2123 vtl->magic_scissors_added_track_name = g_strdup(new_name);
2128 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2130 *l = g_list_append(*l, (gpointer)name);
2133 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2135 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2136 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2138 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2139 vik_trw_layer_delete_track(vtl_src, name);
2140 vik_trw_layer_add_track(vtl_dest, newname, t);
2142 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2144 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2145 vik_trw_layer_delete_waypoint(vtl_src, name);
2146 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2150 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2152 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2153 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2155 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2156 GList *items = NULL;
2159 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2160 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2162 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2163 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2168 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2169 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2171 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2178 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2179 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2184 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2186 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2187 gboolean was_visible = FALSE;
2191 was_visible = t->visible;
2192 if ( t == vtl->current_track )
2193 vtl->current_track = NULL;
2194 if ( t == vtl->magic_scissors_current_track )
2195 vtl->magic_scissors_current_track = NULL;
2197 /* could be current_tp, so we have to check */
2198 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2200 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2201 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2202 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2204 /* do this last because trk_name may be pointing to actual orig key */
2205 g_hash_table_remove ( vtl->tracks, trk_name );
2210 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2212 gboolean was_visible = FALSE;
2215 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2219 if ( wp == vtl->current_wp ) {
2220 vtl->current_wp = NULL;
2221 vtl->current_wp_name = NULL;
2222 vtl->moving_wp = FALSE;
2225 was_visible = wp->visible;
2226 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2227 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2228 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2230 highest_wp_number_remove_wp(vtl, wp_name);
2231 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2237 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2239 vik_treeview_item_delete (vt, it );
2242 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2245 vtl->current_track = NULL;
2246 vtl->magic_scissors_current_track = NULL;
2247 if (vtl->current_tp_track_name)
2248 trw_layer_cancel_current_tp(vtl, FALSE);
2249 if (vtl->last_tp_track_name)
2250 trw_layer_cancel_last_tp ( vtl );
2252 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2253 g_hash_table_remove_all(vtl->tracks_iters);
2254 g_hash_table_remove_all(vtl->tracks);
2256 /* TODO: only update if the layer is visible (ticked) */
2257 vik_layer_emit_update ( VIK_LAYER(vtl) );
2260 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2262 vtl->current_wp = NULL;
2263 vtl->current_wp_name = NULL;
2264 vtl->moving_wp = FALSE;
2266 highest_wp_number_reset(vtl);
2268 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2269 g_hash_table_remove_all(vtl->waypoints_iters);
2270 g_hash_table_remove_all(vtl->waypoints);
2272 /* TODO: only update if the layer is visible (ticked) */
2273 vik_layer_emit_update ( VIK_LAYER(vtl) );
2276 static void trw_layer_delete_item ( gpointer pass_along[5] )
2278 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2279 gboolean was_visible = FALSE;
2280 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2282 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2286 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2289 vik_layer_emit_update ( VIK_LAYER(vtl) );
2293 static void trw_layer_properties_item ( gpointer pass_along[5] )
2295 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2296 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2298 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2301 if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) )
2303 if ( VIK_LAYER(vtl)->visible )
2304 vik_layer_emit_update ( VIK_LAYER(vtl) );
2309 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2312 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2314 pass_along[1], /* vlp */
2315 pass_along[3] /* track name */);
2320 static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord )
2322 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord );
2323 vik_layers_panel_emit_update ( vlp );
2326 static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] )
2328 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2329 if ( trps && trps->data )
2330 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2333 static void trw_layer_goto_track_center ( gpointer pass_along[5] )
2335 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2336 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2337 if ( trps && *trps )
2339 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2341 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2342 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2343 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2344 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2345 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord);
2349 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2351 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2352 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2354 vtl->current_track = track;
2355 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2357 if ( track->trackpoints )
2358 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2362 * extend a track using magic scissors
2364 static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2366 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2367 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2368 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2370 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2371 vtl->magic_scissors_coord = last_coord;
2372 vtl->magic_scissors_current_track = track;
2373 vtl->magic_scissors_started = TRUE;
2375 if ( track->trackpoints )
2376 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &last_coord) ;
2380 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2382 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2383 /* Also warn if overwrite old elevation data */
2384 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2386 vik_track_apply_dem_data ( track );
2389 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2391 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2394 trps = g_list_last(trps);
2395 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2399 static void trw_layer_goto_track_max_speed ( gpointer pass_along[5] )
2401 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2404 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2407 static void trw_layer_goto_track_max_alt ( gpointer pass_along[5] )
2409 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2412 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2415 static void trw_layer_goto_track_min_alt ( gpointer pass_along[5] )
2417 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2420 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2424 * Automatically change the viewport to center on the track and zoom to see the extent of the track
2426 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2428 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2429 if ( trps && *trps )
2431 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2432 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2434 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(pass_along[1])), maxmin );
2435 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2439 /*************************************
2440 * merge/split by time routines
2441 *************************************/
2443 /* called for each key in track hash table.
2444 * If the current track has time stamp, add it to the result,
2445 * except the one pointed by "exclude".
2446 * set exclude to NULL if there is no exclude to check.
2447 * Not that result is in reverse (for performance reason).
2453 static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2455 twt_udata *user_data = udata;
2456 VikTrackpoint *p1, *p2;
2458 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2462 if (VIK_TRACK(value)->trackpoints) {
2463 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2464 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2466 if (!p1->has_timestamp || !p2->has_timestamp) {
2467 g_print("no timestamp\n");
2473 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2476 /* called for each key in track hash table. if original track user_data[1] is close enough
2477 * to the passed one, add it to list in user_data[0]
2479 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2482 VikTrackpoint *p1, *p2;
2484 GList **nearby_tracks = ((gpointer *)user_data)[0];
2485 GList *orig_track = ((gpointer *)user_data)[1];
2486 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
2489 * detect reasons for not merging, and return
2490 * if no reason is found not to merge, then do it.
2493 if (VIK_TRACK(value)->trackpoints == orig_track) {
2497 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2498 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2500 if (VIK_TRACK(value)->trackpoints) {
2501 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2502 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2504 if (!p1->has_timestamp || !p2->has_timestamp) {
2505 g_print("no timestamp\n");
2509 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2510 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2512 abs(p1->timestamp - t2) < thr*60)
2519 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2522 /* comparison function used to sort tracks; a and b are hash table keys */
2523 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2525 GHashTable *tracks = user_data;
2528 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2529 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2531 if (t1 < t2) return -1;
2532 if (t1 > t2) return 1;
2536 /* comparison function used to sort trackpoints */
2537 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2539 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2541 if (t1 < t2) return -1;
2542 if (t1 > t2) return 1;
2546 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2548 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2549 gchar *orig_track_name = pass_along[3];
2550 GList *tracks_with_timestamp = NULL;
2551 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2553 if (track->trackpoints &&
2554 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2555 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2562 udata.result = &tracks_with_timestamp;
2563 udata.exclude = track->trackpoints;
2564 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2565 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2568 if (!tracks_with_timestamp) {
2569 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2573 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2574 vtl->tracks, tracks_with_timestamp, TRUE,
2575 _("Merge with..."), _("Select track to merge with"));
2576 g_list_free(tracks_with_timestamp);
2581 for (l = merge_list; l != NULL; l = g_list_next(l)) {
2582 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2584 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2585 merge_track->trackpoints = NULL;
2586 vik_trw_layer_delete_track(vtl, l->data);
2587 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2590 /* TODO: free data before free merge_list */
2591 for (l = merge_list; l != NULL; l = g_list_next(l))
2593 g_list_free(merge_list);
2594 vik_layer_emit_update( VIK_LAYER(vtl) );
2598 /* merge by time routine */
2599 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2601 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2602 gchar *orig_track_name = strdup(pass_along[3]);
2605 GList *nearby_tracks;
2608 static guint thr = 1;
2609 guint track_count = 0;
2611 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2612 _("Merge Threshold..."),
2613 _("Merge when time between tracks less than:"),
2615 free(orig_track_name);
2619 /* merge tracks until we can't */
2620 nearby_tracks = NULL;
2624 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2625 trps = track->trackpoints;
2630 if (nearby_tracks) {
2631 g_list_free(nearby_tracks);
2632 nearby_tracks = NULL;
2635 t1 = ((VikTrackpoint *)trps->data)->timestamp;
2636 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
2638 /* g_print("Original track times: %d and %d\n", t1, t2); */
2639 params[0] = &nearby_tracks;
2641 params[2] = GUINT_TO_POINTER (thr);
2643 /* get a list of adjacent-in-time tracks */
2644 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
2646 /* add original track */
2647 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
2651 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
2652 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
2653 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
2654 GList *l = nearby_tracks;
2655 VikTrack *tr = vik_track_new();
2656 tr->visible = track->visible;
2661 t1 = get_first_trackpoint(l)->timestamp;
2662 t2 = get_last_trackpoint(l)->timestamp;
2663 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
2667 /* remove trackpoints from merged track, delete track */
2668 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
2669 get_track(l)->trackpoints = NULL;
2670 vik_trw_layer_delete_track(vtl, l->data);
2675 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
2676 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
2678 #undef get_first_trackpoint
2679 #undef get_last_trackpoint
2682 } while (track_count > 1);
2683 g_list_free(nearby_tracks);
2684 free(orig_track_name);
2685 vik_layer_emit_update( VIK_LAYER(vtl) );
2688 /* split by time routine */
2689 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
2691 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2692 GList *trps = track->trackpoints;
2694 GList *newlists = NULL;
2695 GList *newtps = NULL;
2697 static guint thr = 1;
2704 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2705 _("Split Threshold..."),
2706 _("Split when time between trackpoints exceeds:"),
2711 /* iterate through trackpoints, and copy them into new lists without touching original list */
2712 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
2716 ts = VIK_TRACKPOINT(iter->data)->timestamp;
2718 g_print("panic: ts < prev_ts: this should never happen!\n");
2721 if (ts - prev_ts > thr*60) {
2722 /* flush accumulated trackpoints into new list */
2723 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2727 /* accumulate trackpoint copies in newtps, in reverse order */
2728 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
2730 iter = g_list_next(iter);
2733 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2736 /* put lists of trackpoints into tracks */
2743 tr = vik_track_new();
2744 tr->visible = track->visible;
2745 tr->trackpoints = (GList *)(iter->data);
2747 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
2748 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
2749 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
2750 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
2752 iter = g_list_next(iter);
2754 g_list_free(newlists);
2755 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
2756 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2759 /* end of split/merge routines */
2762 static void trw_layer_goto_waypoint ( gpointer pass_along[5] )
2764 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2766 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) );
2769 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] )
2771 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
2772 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
2776 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2778 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2783 if (strcmp(newname, sublayer) == 0 )
2786 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
2787 if (g_hash_table_lookup( l->waypoints, newname))
2789 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
2794 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
2795 g_hash_table_steal ( l->waypoints_iters, sublayer );
2797 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
2798 highest_wp_number_remove_wp(l, sublayer);
2799 g_hash_table_remove ( l->waypoints, sublayer );
2801 rv = g_strdup(newname);
2803 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2805 highest_wp_number_add_wp(l, rv);
2806 g_hash_table_insert ( l->waypoints, rv, wp );
2807 g_hash_table_insert ( l->waypoints_iters, rv, iter );
2809 /* it hasn't been updated yet so we pass new name */
2810 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2811 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2814 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2817 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2824 if (strcmp(newname, sublayer) == 0)
2827 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
2828 if (g_hash_table_lookup( l->tracks, newname))
2830 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
2835 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
2836 g_hash_table_steal ( l->tracks, sublayer );
2838 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
2839 g_hash_table_steal ( l->tracks_iters, sublayer );
2841 rv = g_strdup(newname);
2843 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2845 g_hash_table_insert ( l->tracks, rv, tr );
2846 g_hash_table_insert ( l->tracks_iters, rv, iter );
2848 /* don't forget about current_tp_track_name, update that too */
2849 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
2851 l->current_tp_track_name = rv;
2853 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
2855 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
2856 l->last_tp_track_name = rv;
2858 g_free ( orig_key );
2860 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2861 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2864 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2870 static gboolean is_valid_geocache_name ( gchar *str )
2872 gint len = strlen ( str );
2873 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]));
2876 static void trw_layer_track_use_with_filter ( gpointer *pass_along )
2878 gchar *track_name = (gchar *) pass_along[3];
2879 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
2880 a_acquire_set_filter_track ( tr, track_name );
2883 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
2885 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
2886 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
2889 static void trw_layer_track_google_route_webpage( gpointer *pass_along )
2891 gchar *track_name = (gchar *) pass_along[3];
2892 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
2894 gchar *escaped = uri_escape ( tr->comment );
2895 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
2896 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
2902 /* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */
2903 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2905 static GtkTreeIter staticiter;
2906 static gpointer pass_along[5];
2908 gboolean rv = FALSE;
2911 pass_along[1] = vlp;
2912 pass_along[2] = GINT_TO_POINTER (subtype);
2913 pass_along[3] = sublayer;
2914 staticiter = *iter; /* will exist after function has ended */
2915 pass_along[4] = &staticiter;
2917 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2921 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
2922 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
2923 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2924 gtk_widget_show ( item );
2926 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2927 VikTrwLayer *vtl = l;
2928 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
2929 if (tr && tr->property_dialog)
2930 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
2933 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
2934 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
2935 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2936 gtk_widget_show ( item );
2938 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
2939 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
2940 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2941 gtk_widget_show ( item );
2943 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
2944 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
2945 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2946 gtk_widget_show ( item );
2948 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2950 /* could be a right-click using the tool */
2951 if ( vlp != NULL ) {
2952 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
2953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
2954 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2955 gtk_widget_show ( item );
2958 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
2960 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
2961 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
2962 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2963 gtk_widget_show ( item );
2969 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2971 GtkWidget *goto_submenu;
2972 item = gtk_menu_item_new ();
2973 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2974 gtk_widget_show ( item );
2976 goto_submenu = gtk_menu_new ();
2977 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
2978 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2979 gtk_widget_show ( item );
2980 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
2982 item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
2983 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
2984 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2985 gtk_widget_show ( item );
2987 item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
2988 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
2989 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2990 gtk_widget_show ( item );
2992 item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
2993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
2994 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2995 gtk_widget_show ( item );
2997 item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
2998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
2999 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3000 gtk_widget_show ( item );
3002 item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3004 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3005 gtk_widget_show ( item );
3007 item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3009 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3010 gtk_widget_show ( item );
3012 item = gtk_menu_item_new_with_mnemonic ( _("_View Track") );
3013 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3014 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3015 gtk_widget_show ( item );
3017 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time") );
3018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3019 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3020 gtk_widget_show ( item );
3022 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3024 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3025 gtk_widget_show ( item );
3027 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time") );
3028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3029 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3030 gtk_widget_show ( item );
3032 item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3034 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3035 gtk_widget_show ( item );
3037 item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3039 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3040 gtk_widget_show ( item );
3042 item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX") );
3043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3044 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3045 gtk_widget_show ( item );
3047 item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3049 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3050 gtk_widget_show ( item );
3052 item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Magic Scissors") );
3053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
3054 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3055 gtk_widget_show ( item );
3057 #ifdef VIK_CONFIG_OPENSTREETMAP
3058 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
3059 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3060 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3061 gtk_widget_show ( item );
3064 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3066 item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3068 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3069 gtk_widget_show ( item );
3072 item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
3073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3074 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3075 gtk_widget_show ( item );
3077 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3078 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3079 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3081 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3082 gtk_widget_show ( item );
3086 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3088 item = gtk_menu_item_new ();
3089 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3090 gtk_widget_show ( item );
3092 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
3093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3094 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3095 gtk_widget_show ( item );
3101 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3104 if (!vtl->current_tpl)
3106 if (!vtl->current_tpl->next)
3109 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3110 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3112 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3115 VikTrackpoint *tp_new = vik_trackpoint_new();
3116 struct LatLon ll_current, ll_next;
3117 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3118 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3120 /* main positional interpolation */
3121 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3122 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3124 /* Now other properties that can be interpolated */
3125 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3127 if (tp_current->has_timestamp && tp_next->has_timestamp) {
3128 /* Note here the division is applied to each part, then added
3129 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3130 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3131 tp_new->has_timestamp = TRUE;
3134 if (tp_current->speed != NAN && tp_next->speed != NAN)
3135 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3137 /* TODO - improve interpolation of course, as it may not be correct.
3138 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3139 [similar applies if value is in radians] */
3140 if (tp_current->course != NAN && tp_next->course != NAN)
3141 tp_new->speed = (tp_current->course + tp_next->course)/2;
3143 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3145 /* Insert new point into the trackpoints list after the current TP */
3146 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3147 gint index = g_list_index ( tr->trackpoints, tp_current );
3149 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3154 /* to be called when last_tpl no long exists. */
3155 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3157 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3158 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3159 vtl->last_tpl = NULL;
3160 vtl->last_tp_track_name = NULL;
3163 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3169 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3173 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3175 if ( vtl->current_tpl )
3177 vtl->current_tpl = NULL;
3178 vtl->current_tp_track_name = NULL;
3179 vik_layer_emit_update(VIK_LAYER(vtl));
3183 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3185 g_assert ( vtl->tpwin != NULL );
3186 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3187 trw_layer_cancel_current_tp ( vtl, TRUE );
3188 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3191 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, NULL ) ) )
3193 VikTrack *tr = vik_track_new ();
3194 GList *newglist = g_list_alloc ();
3195 newglist->prev = NULL;
3196 newglist->next = vtl->current_tpl->next;
3197 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3198 tr->trackpoints = newglist;
3200 vtl->current_tpl->next->prev = newglist; /* end old track here */
3201 vtl->current_tpl->next = NULL;
3203 vtl->current_tpl = newglist; /* change tp to first of new track. */
3204 vtl->current_tp_track_name = name;
3206 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3210 vik_trw_layer_add_track ( vtl, name, tr );
3211 vik_layer_emit_update(VIK_LAYER(vtl));
3214 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3216 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3218 g_assert(tr != NULL);
3220 /* can't join with a non-existent trackpoint */
3221 vtl->last_tpl = NULL;
3222 vtl->last_tp_track_name = NULL;
3224 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3226 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3227 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3229 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3231 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3232 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3234 trw_layer_cancel_last_tp ( vtl );
3236 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3237 g_list_free_1 ( vtl->current_tpl );
3238 vtl->current_tpl = new_tpl;
3239 vik_layer_emit_update(VIK_LAYER(vtl));
3243 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3244 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3245 g_list_free_1 ( vtl->current_tpl );
3246 trw_layer_cancel_current_tp ( vtl, FALSE );
3249 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3251 vtl->last_tpl = vtl->current_tpl;
3252 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3253 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3255 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3257 vtl->last_tpl = vtl->current_tpl;
3258 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3259 vik_layer_emit_update(VIK_LAYER(vtl));
3261 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
3263 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
3264 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3266 VikTrack *tr_first = tr1, *tr_last = tr2;
3270 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
3271 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
3272 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
3273 vik_track_reverse ( tr1 );
3274 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
3279 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
3281 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. */
3282 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
3283 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
3284 tr2->trackpoints = NULL;
3286 tmp = vtl->current_tp_track_name;
3288 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
3289 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3291 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
3292 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
3293 vik_trw_layer_delete_track ( vtl, tmp );
3295 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
3296 vik_layer_emit_update(VIK_LAYER(vtl));
3298 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
3300 trw_layer_insert_tp_after_current_tp ( vtl );
3301 vik_layer_emit_update(VIK_LAYER(vtl));
3303 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
3304 vik_layer_emit_update (VIK_LAYER(vtl));
3307 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
3311 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
3312 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
3313 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
3314 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
3315 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
3317 if ( vtl->current_tpl )
3318 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3319 /* set layer name and TP data */
3322 /***************************************************************************
3324 ***************************************************************************/
3326 /*** Utility data structures and functions ****/
3330 gint closest_x, closest_y;
3331 gchar *closest_wp_name;
3332 VikWaypoint *closest_wp;
3338 gint closest_x, closest_y;
3339 gchar *closest_track_name;
3340 VikTrackpoint *closest_tp;
3345 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
3351 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
3353 if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
3354 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
3355 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3357 params->closest_wp_name = name;
3358 params->closest_wp = wp;
3359 params->closest_x = x;
3360 params->closest_y = y;
3364 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
3366 GList *tpl = t->trackpoints;
3375 tp = VIK_TRACKPOINT(tpl->data);
3377 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
3379 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
3380 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
3381 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3383 params->closest_track_name = name;
3384 params->closest_tp = tp;
3385 params->closest_tpl = tpl;
3386 params->closest_x = x;
3387 params->closest_y = y;
3393 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3395 TPSearchParams params;
3399 params.closest_track_name = NULL;
3400 params.closest_tp = NULL;
3401 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3402 return params.closest_tp;
3405 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3407 WPSearchParams params;
3411 params.closest_wp = NULL;
3412 params.closest_wp_name = NULL;
3413 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3414 return params.closest_wp;
3417 /* background drawing hook, to be passed the viewport */
3418 static gboolean tool_sync_done = TRUE;
3420 static gboolean tool_sync(gpointer data)
3422 VikViewport *vvp = data;
3423 gdk_threads_enter();
3424 vik_viewport_sync(vvp);
3425 tool_sync_done = TRUE;
3426 gdk_threads_leave();
3437 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
3440 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
3441 gdk_gc_set_function ( t->gc, GDK_INVERT );
3442 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3443 vik_viewport_sync(t->vvp);
3448 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
3450 VikViewport *vvp = t->vvp;
3451 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3452 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3456 if (tool_sync_done) {
3457 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
3458 tool_sync_done = FALSE;
3462 static void marker_end_move ( tool_ed_t *t )
3464 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3465 g_object_unref ( t->gc );
3469 /*** Edit waypoint ****/
3471 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3473 tool_ed_t *t = g_new(tool_ed_t, 1);
3479 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3481 WPSearchParams params;
3482 tool_ed_t *t = data;
3483 VikViewport *vvp = t->vvp;
3485 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3492 if ( !vtl->vl.visible || !vtl->waypoints_visible )
3495 if ( vtl->current_wp && vtl->current_wp->visible )
3497 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
3499 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
3501 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
3502 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
3504 if ( event->button == 3 )
3505 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3507 marker_begin_move(t, event->x, event->y);
3514 params.x = event->x;
3515 params.y = event->y;
3516 params.closest_wp_name = NULL;
3517 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3518 params.closest_wp = NULL;
3519 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3520 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
3522 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
3523 marker_begin_move(t, event->x, event->y);
3524 g_critical("shouldn't be here");
3527 else if ( params.closest_wp )
3529 if ( event->button == 3 )
3530 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3532 vtl->waypoint_rightclick = FALSE;
3534 vtl->current_wp = params.closest_wp;
3535 vtl->current_wp_name = params.closest_wp_name;
3537 if ( params.closest_wp )
3538 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
3540 /* could make it so don't update if old WP is off screen and new is null but oh well */
3541 vik_layer_emit_update ( VIK_LAYER(vtl) );
3545 vtl->current_wp = NULL;
3546 vtl->current_wp_name = NULL;
3547 vtl->waypoint_rightclick = FALSE;
3548 vik_layer_emit_update ( VIK_LAYER(vtl) );
3552 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
3554 tool_ed_t *t = data;
3555 VikViewport *vvp = t->vvp;
3557 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3562 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3565 if ( event->state & GDK_CONTROL_MASK )
3567 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3569 new_coord = tp->coord;
3573 if ( event->state & GDK_SHIFT_MASK )
3575 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3576 if ( wp && wp != vtl->current_wp )
3577 new_coord = wp->coord;
3582 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3584 marker_moveto ( t, x, y );
3591 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3593 tool_ed_t *t = data;
3594 VikViewport *vvp = t->vvp;
3596 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3599 if ( t->holding && event->button == 1 )
3602 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3605 if ( event->state & GDK_CONTROL_MASK )
3607 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3609 new_coord = tp->coord;
3613 if ( event->state & GDK_SHIFT_MASK )
3615 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3616 if ( wp && wp != vtl->current_wp )
3617 new_coord = wp->coord;
3620 marker_end_move ( t );
3622 vtl->current_wp->coord = new_coord;
3623 vik_layer_emit_update ( VIK_LAYER(vtl) );
3626 /* PUT IN RIGHT PLACE!!! */
3627 if ( event->button == 3 && vtl->waypoint_rightclick )
3629 if ( vtl->wp_right_click_menu )
3630 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
3631 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
3632 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 ) );
3633 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
3634 vtl->waypoint_rightclick = FALSE;
3639 /**** Begin track ***/
3640 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
3645 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3647 vtl->current_track = NULL;
3648 return tool_new_track_click ( vtl, event, vvp );
3651 /*** New track ****/
3653 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
3662 } new_track_move_passalong_t;
3664 /* sync and undraw, but only when we have time */
3665 static gboolean ct_sync ( gpointer passalong )
3667 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
3668 vik_viewport_sync ( p->vvp );
3669 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
3670 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
3671 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
3672 p->vtl->ct_sync_done = TRUE;
3677 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
3679 /* if we haven't sync'ed yet, we don't have time to do more. */
3680 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
3681 GList *iter = vtl->current_track->trackpoints;
3682 new_track_move_passalong_t *passalong;
3685 while ( iter->next )
3687 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
3688 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
3689 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
3690 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
3692 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
3693 passalong->vtl = vtl;
3694 passalong->vvp = vvp;
3697 passalong->x2 = event->x;
3698 passalong->y2 = event->y;
3700 /* this will sync and undraw when we have time to */
3701 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
3702 vtl->ct_sync_done = FALSE;
3703 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
3705 return VIK_LAYER_TOOL_ACK;
3708 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
3710 if ( vtl->current_track && event->keyval == GDK_Escape ) {
3711 vtl->current_track = NULL;
3712 vik_layer_emit_update ( VIK_LAYER(vtl) );
3714 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
3716 if ( vtl->current_track->trackpoints )
3718 GList *last = g_list_last(vtl->current_track->trackpoints);
3719 g_free ( last->data );
3720 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3722 vik_layer_emit_update ( VIK_LAYER(vtl) );
3728 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3732 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3735 if ( event->button == 3 && vtl->current_track )
3738 if ( vtl->current_track->trackpoints )
3740 GList *last = g_list_last(vtl->current_track->trackpoints);
3741 g_free ( last->data );
3742 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3744 vik_layer_emit_update ( VIK_LAYER(vtl) );
3748 if ( event->type == GDK_2BUTTON_PRESS )
3750 /* subtract last (duplicate from double click) tp then end */
3751 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
3753 GList *last = g_list_last(vtl->current_track->trackpoints);
3754 g_free ( last->data );
3755 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3756 /* undo last, then end */
3757 vtl->current_track = NULL;
3759 vik_layer_emit_update ( VIK_LAYER(vtl) );
3763 if ( ! vtl->current_track )
3765 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
3766 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
3768 vtl->current_track = vik_track_new();
3769 vtl->current_track->visible = TRUE;
3770 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3772 /* incase it was created by begin track */
3773 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3778 tp = vik_trackpoint_new();
3779 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
3781 /* snap to other TP */
3782 if ( event->state & GDK_CONTROL_MASK )
3784 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3786 tp->coord = other_tp->coord;
3789 tp->newsegment = FALSE;
3790 tp->has_timestamp = FALSE;
3792 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
3794 vtl->ct_x1 = vtl->ct_x2;
3795 vtl->ct_y1 = vtl->ct_y2;
3796 vtl->ct_x2 = event->x;
3797 vtl->ct_y2 = event->y;
3799 vik_layer_emit_update ( VIK_LAYER(vtl) );
3804 /*** New waypoint ****/
3806 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3811 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3814 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3816 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
3817 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
3818 vik_layer_emit_update ( VIK_LAYER(vtl) );
3823 /*** Edit trackpoint ****/
3825 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
3827 tool_ed_t *t = g_new(tool_ed_t, 1);
3833 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3835 tool_ed_t *t = data;
3836 VikViewport *vvp = t->vvp;
3837 TPSearchParams params;
3838 /* OUTDATED DOCUMENTATION:
3839 find 5 pixel range on each side. then put these UTM, and a pointer
3840 to the winning track name (and maybe the winning track itself), and a
3841 pointer to the winning trackpoint, inside an array or struct. pass
3842 this along, do a foreach on the tracks which will do a foreach on the
3845 params.x = event->x;
3846 params.y = event->y;
3847 params.closest_track_name = NULL;
3848 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3849 params.closest_tp = NULL;
3851 if ( event->button != 1 )
3854 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3857 if ( !vtl->vl.visible || !vtl->tracks_visible )
3860 if ( vtl->current_tpl )
3862 /* 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.) */
3863 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
3864 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
3866 g_assert ( current_tr );
3868 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
3870 if ( current_tr->visible &&
3871 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
3872 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
3873 marker_begin_move ( t, event->x, event->y );
3877 vtl->last_tpl = vtl->current_tpl;
3878 vtl->last_tp_track_name = vtl->current_tp_track_name;
3881 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3883 if ( params.closest_tp )
3885 vtl->current_tpl = params.closest_tpl;
3886 vtl->current_tp_track_name = params.closest_track_name;
3887 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
3888 trw_layer_tpwin_init ( vtl );
3889 vik_layer_emit_update ( VIK_LAYER(vtl) );
3893 /* these aren't the droids you're looking for */
3897 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
3899 tool_ed_t *t = data;
3900 VikViewport *vvp = t->vvp;
3902 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3908 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3911 if ( event->state & GDK_CONTROL_MASK )
3913 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3914 if ( tp && tp != vtl->current_tpl->data )
3915 new_coord = tp->coord;
3917 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3920 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3921 marker_moveto ( t, x, y );
3929 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3931 tool_ed_t *t = data;
3932 VikViewport *vvp = t->vvp;
3934 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3936 if ( event->button != 1)
3941 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3944 if ( event->state & GDK_CONTROL_MASK )
3946 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3947 if ( tp && tp != vtl->current_tpl->data )
3948 new_coord = tp->coord;
3951 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3953 marker_end_move ( t );
3955 /* diff dist is diff from orig */
3956 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3957 /* can't join with itself! */
3958 trw_layer_cancel_last_tp ( vtl );
3960 vik_layer_emit_update ( VIK_LAYER(vtl) );
3967 /*** Magic Scissors ***/
3968 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
3973 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3976 if ( !vtl ) return FALSE;
3977 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
3978 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
3980 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
3982 vtl->magic_scissors_coord = *new_end;
3984 vik_layer_emit_update ( VIK_LAYER(vtl) );
3985 /* remove last ' to:...' */
3986 if ( vtl->magic_scissors_current_track->comment ) {
3987 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
3988 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
3989 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
3990 last_to - vtl->magic_scissors_current_track->comment - 1);
3991 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
3996 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
3997 struct LatLon start, end;
3998 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
3999 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
4002 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
4003 vik_coord_to_latlon ( &(tmp), &end );
4004 vtl->magic_scissors_coord = tmp; /* for continuations */
4006 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
4007 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
4008 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
4010 vtl->magic_scissors_check_added_track = TRUE;
4011 vtl->magic_scissors_started = FALSE;
4014 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
4015 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
4016 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
4017 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
4018 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
4019 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
4022 /* see if anything was done -- a track was added or appended to */
4023 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
4026 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
4029 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
4031 vtl->magic_scissors_current_track = tr;
4033 g_free ( vtl->magic_scissors_added_track_name );
4034 vtl->magic_scissors_added_track_name = NULL;
4035 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
4036 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
4037 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
4038 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4040 vtl->magic_scissors_check_added_track = FALSE;
4041 vtl->magic_scissors_append = FALSE;
4043 vik_layer_emit_update ( VIK_LAYER(vtl) );
4045 vtl->magic_scissors_started = TRUE;
4046 vtl->magic_scissors_coord = tmp;
4047 vtl->magic_scissors_current_track = NULL;
4052 /*** Show picture ****/
4054 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
4059 /* Params are: vvp, event, last match found or NULL */
4060 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
4062 if ( wp->image && wp->visible )
4064 gint x, y, slackx, slacky;
4065 GdkEventButton *event = (GdkEventButton *) params[1];
4067 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
4068 slackx = wp->image_width / 2;
4069 slacky = wp->image_height / 2;
4070 if ( x <= event->x + slackx && x >= event->x - slackx
4071 && y <= event->y + slacky && y >= event->y - slacky )
4073 params[2] = wp->image; /* we've found a match. however continue searching
4074 * since we want to find the last match -- that
4075 * is, the match that was drawn last. */
4080 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4082 gpointer params[3] = { vvp, event, NULL };
4083 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4085 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
4088 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
4090 ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
4093 gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
4094 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
4095 g_free ( quoted_file );
4096 if ( ! g_spawn_command_line_async ( cmd, &err ) )
4098 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch eog to open file.") );
4099 g_error_free ( err );
4102 #endif /* WINDOWS */
4103 return TRUE; /* found a match */
4106 return FALSE; /* go through other layers, searching for a match */
4109 /***************************************************************************
4111 ***************************************************************************/
4117 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
4119 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
4120 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
4123 static void create_thumbnails_thread ( GSList *pics, gpointer threaddata )
4125 guint total = g_slist_length(pics), done = 0;
4128 a_thumbnails_create ( (gchar *) pics->data );
4129 a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
4134 static void free_pics_slist ( GSList *pics )
4138 g_free ( pics->data );
4139 pics = g_slist_delete_link ( pics, pics );
4143 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
4145 if ( ! vtl->has_verified_thumbnails )
4147 GSList *pics = NULL;
4148 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
4151 gint len = g_slist_length ( pics );
4152 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
4153 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 );
4159 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
4161 return vtl->coord_mode;
4166 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
4168 vik_coord_convert ( &(wp->coord), *dest_mode );
4171 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
4173 vik_track_convert ( tr, *dest_mode );
4176 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
4178 if ( vtl->coord_mode != dest_mode )
4180 vtl->coord_mode = dest_mode;
4181 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
4182 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
4186 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
4188 return g_hash_table_lookup ( vtl->waypoints, name );
4191 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
4193 return g_hash_table_lookup ( vtl->tracks, name );
4196 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
4198 vtl->menu_selection = selection;
4201 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
4203 return(vtl->menu_selection);
4206 /* ----------- Downloading maps along tracks --------------- */
4208 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
4210 /* TODO: calculating based on current size of viewport */
4211 const gdouble w_at_zoom_0_125 = 0.0013;
4212 const gdouble h_at_zoom_0_125 = 0.0011;
4213 gdouble zoom_factor = zoom_level/0.125;
4215 wh->lat = h_at_zoom_0_125 * zoom_factor;
4216 wh->lon = w_at_zoom_0_125 * zoom_factor;
4218 return 0; /* all OK */
4221 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
4223 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
4224 (dist->lat >= ABS(to->north_south - from->north_south)))
4227 VikCoord *coord = g_malloc(sizeof(VikCoord));
4228 coord->mode = VIK_COORD_LATLON;
4230 if (ABS(gradient) < 1) {
4231 if (from->east_west > to->east_west)
4232 coord->east_west = from->east_west - dist->lon;
4234 coord->east_west = from->east_west + dist->lon;
4235 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
4237 if (from->north_south > to->north_south)
4238 coord->north_south = from->north_south - dist->lat;
4240 coord->north_south = from->north_south + dist->lat;
4241 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
4247 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
4249 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
4250 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
4252 VikCoord *next = from;
4254 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
4256 list = g_list_prepend(list, next);
4262 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
4264 typedef struct _Rect {
4269 #define GLRECT(iter) ((Rect *)((iter)->data))
4272 GList *rects_to_download = NULL;
4275 if (get_download_area_width(vvp, zoom_level, &wh))
4278 GList *iter = tr->trackpoints;
4282 gboolean new_map = TRUE;
4283 VikCoord *cur_coord, tl, br;
4286 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
4288 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4289 rect = g_malloc(sizeof(Rect));
4292 rect->center = *cur_coord;
4293 rects_to_download = g_list_prepend(rects_to_download, rect);
4298 gboolean found = FALSE;
4299 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4300 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
4311 GList *fillins = NULL;
4312 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
4313 /* seems that ATM the function get_next_coord works only for LATLON */
4314 if ( cur_coord->mode == VIK_COORD_LATLON ) {
4315 /* fill-ins for far apart points */
4316 GList *cur_rect, *next_rect;
4317 for (cur_rect = rects_to_download;
4318 (next_rect = cur_rect->next) != NULL;
4319 cur_rect = cur_rect->next) {
4320 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
4321 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
4322 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
4326 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
4329 GList *iter = fillins;
4331 cur_coord = (VikCoord *)(iter->data);
4332 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4333 rect = g_malloc(sizeof(Rect));
4336 rect->center = *cur_coord;
4337 rects_to_download = g_list_prepend(rects_to_download, rect);
4342 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4343 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
4347 for (iter = fillins; iter; iter = iter->next)
4349 g_list_free(fillins);
4351 if (rects_to_download) {
4352 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
4353 g_free(rect_iter->data);
4354 g_list_free(rects_to_download);
4358 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6])
4361 gint selected_map, default_map;
4362 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
4363 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
4364 gint selected_zoom, default_zoom;
4368 VikTrwLayer *vtl = pass_along[0];
4369 VikLayersPanel *vlp = pass_along[1];
4370 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4371 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
4373 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS);
4374 int num_maps = g_list_length(vmls);
4377 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
4381 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
4382 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
4384 gchar **np = map_names;
4385 VikMapsLayer **lp = map_layers;
4386 for (i = 0; i < num_maps; i++) {
4387 gboolean dup = FALSE;
4388 vml = (VikMapsLayer *)(vmls->data);
4389 for (j = 0; j < i; j++) { /* no duplicate allowed */
4390 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
4397 *np++ = vik_maps_layer_get_map_label(vml);
4403 num_maps = lp - map_layers;
4405 for (default_map = 0; default_map < num_maps; default_map++) {
4406 /* TODO: check for parent layer's visibility */
4407 if (VIK_LAYER(map_layers[default_map])->visible)
4410 default_map = (default_map == num_maps) ? 0 : default_map;
4412 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
4413 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
4414 if (cur_zoom == zoom_vals[default_zoom])
4417 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
4419 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
4422 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
4425 for (i = 0; i < num_maps; i++)
4426 g_free(map_names[i]);
4434 /**** lowest waypoint number calculation ***/
4435 static gint highest_wp_number_name_to_number(const gchar *name) {
4436 if ( strlen(name) == 3 ) {
4438 if ( n < 100 && name[0] != '0' )
4440 if ( n < 10 && name[0] != '0' )
4448 static void highest_wp_number_reset(VikTrwLayer *vtl)
4450 vtl->highest_wp_number = -1;
4453 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
4455 /* if is bigger that top, add it */
4456 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
4457 if ( new_wp_num > vtl->highest_wp_number )
4458 vtl->highest_wp_number = new_wp_num;
4461 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
4463 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
4464 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
4465 if ( vtl->highest_wp_number == old_wp_num ) {
4467 vtl->highest_wp_number --;
4469 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4470 /* search down until we find something that *does* exist */
4472 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
4473 vtl->highest_wp_number --;
4474 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4479 /* get lowest unused number */
4480 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
4483 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
4485 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
4486 return g_strdup(buf);