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_merge_by_timestamp ( gpointer pass_along[6] );
234 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
235 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6]);
236 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
237 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
238 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
239 static void trw_layer_new_wp ( gpointer lav[2] );
240 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
241 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
242 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
245 static void trw_layer_properties_item ( gpointer pass_along[5] );
246 static void trw_layer_goto_waypoint ( gpointer pass_along[5] );
247 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] );
249 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] );
250 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] );
251 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
254 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
255 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
257 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
258 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
260 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
261 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
262 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
263 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
264 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
266 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
267 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
268 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
269 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
270 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
272 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
273 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
274 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
275 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
276 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
277 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
278 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
279 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
280 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
281 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
282 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
283 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
284 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
285 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
286 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
287 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
288 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
289 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
290 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
291 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
294 static void cached_pixbuf_free ( CachedPixbuf *cp );
295 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
296 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
298 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
299 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
301 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
303 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
304 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
305 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
307 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
308 static void highest_wp_number_reset(VikTrwLayer *vtl);
309 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
310 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
313 static VikToolInterface trw_layer_tools[] = {
314 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
315 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
317 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
318 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
319 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
321 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
322 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
324 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
325 (VikToolMouseFunc) tool_edit_waypoint_click,
326 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
327 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
329 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
330 (VikToolMouseFunc) tool_edit_trackpoint_click,
331 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
332 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
334 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
335 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
337 { N_("Magic Scissors"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
338 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
340 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
342 /****** PARAMETERS ******/
344 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
345 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
347 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
348 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
351 static VikLayerParamScale params_scales[] = {
352 /* min max step digits */
353 { 1, 10, 1, 0 }, /* line_thickness */
354 { 0.0, 99.0, 1, 2 }, /* velocity_min */
355 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
356 /* 5 * step == how much to turn */
357 { 16, 128, 3.2, 0 }, /* image_size */
358 { 0, 255, 5, 0 }, /* image alpha */
359 { 5, 500, 5, 0 }, /* image cache_size */
360 { 0, 8, 1, 0 }, /* image cache_size */
361 { 1, 64, 1, 0 }, /* wpsize */
362 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
363 { 1, 100, 1, 0 }, /* stop_length */
366 VikLayerParam trw_layer_params[] = {
367 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
368 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
370 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
371 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
372 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
373 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
374 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
376 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
377 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
379 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
380 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
381 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
382 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
383 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
385 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
386 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
387 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
388 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
389 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
390 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
391 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
392 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
394 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
395 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
396 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
397 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
400 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 };
403 *** 1) Add to trw_layer_params and enumeration
404 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
407 /****** END PARAMETERS ******/
409 VikLayerInterface vik_trw_layer_interface = {
414 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
418 params_groups, /* params_groups */
419 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
423 (VikLayerFuncCreate) vik_trw_layer_create,
424 (VikLayerFuncRealize) vik_trw_layer_realize,
425 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
426 (VikLayerFuncFree) vik_trw_layer_free,
428 (VikLayerFuncProperties) NULL,
429 (VikLayerFuncDraw) vik_trw_layer_draw,
430 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
432 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
433 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
435 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
436 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
438 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
439 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
441 (VikLayerFuncMarshall) trw_layer_marshall,
442 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
444 (VikLayerFuncSetParam) trw_layer_set_param,
445 (VikLayerFuncGetParam) trw_layer_get_param,
447 (VikLayerFuncReadFileData) a_gpspoint_read_file,
448 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
450 (VikLayerFuncDeleteItem) trw_layer_del_item,
451 (VikLayerFuncCopyItem) trw_layer_copy_item,
452 (VikLayerFuncPasteItem) trw_layer_paste_item,
453 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
455 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
458 /* for copy & paste (I think?) */
466 GType vik_trw_layer_get_type ()
468 static GType vtl_type = 0;
472 static const GTypeInfo vtl_info =
474 sizeof (VikTrwLayerClass),
475 NULL, /* base_init */
476 NULL, /* base_finalize */
477 NULL, /* class init */
478 NULL, /* class_finalize */
479 NULL, /* class_data */
480 sizeof (VikTrwLayer),
482 NULL /* instance init */
484 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
490 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
492 static gpointer pass_along[5];
498 pass_along[1] = NULL;
499 pass_along[2] = GINT_TO_POINTER (subtype);
500 pass_along[3] = sublayer;
501 pass_along[4] = NULL;
503 trw_layer_delete_item ( pass_along );
506 static void trw_layer_copy_item_cb( gpointer pass_along[5])
508 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
509 gint subtype = GPOINTER_TO_INT (pass_along[2]);
510 gpointer * sublayer = pass_along[3];
514 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
517 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
522 static void trw_layer_cut_item_cb( gpointer pass_along[5])
524 trw_layer_copy_item_cb(pass_along);
525 trw_layer_delete_item(pass_along);
528 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
539 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
541 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
543 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
546 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
547 fi = g_malloc ( *len );
548 fi->len = strlen(sublayer) + 1;
549 memcpy(fi->data, sublayer, fi->len);
550 memcpy(fi->data + fi->len, id, il);
552 *item = (guint8 *)fi;
555 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
557 FlatItem *fi = (FlatItem *) item;
559 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
564 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
565 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
566 vik_trw_layer_add_waypoint ( vtl, name, w );
567 waypoint_convert(name, w, &vtl->coord_mode);
568 // Consider if redraw necessary for the new item
569 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
570 vik_layer_emit_update ( VIK_LAYER(vtl) );
573 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
577 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
578 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
579 vik_trw_layer_add_track ( vtl, name, t );
580 track_convert(name, t, &vtl->coord_mode);
581 // Consider if redraw necessary for the new item
582 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
583 vik_layer_emit_update ( VIK_LAYER(vtl) );
589 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
596 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
600 case PARAM_TV: vtl->tracks_visible = data.b; break;
601 case PARAM_WV: vtl->waypoints_visible = data.b; break;
602 case PARAM_DM: vtl->drawmode = data.u; break;
603 case PARAM_DP: vtl->drawpoints = data.b; break;
604 case PARAM_DE: vtl->drawelevation = data.b; break;
605 case PARAM_DS: vtl->drawstops = data.b; break;
606 case PARAM_DL: vtl->drawlines = data.b; break;
607 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
608 vtl->stop_length = data.u;
610 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
611 vtl->elevation_factor = data.u;
613 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
615 vtl->line_thickness = data.u;
616 trw_layer_new_track_gcs ( vtl, vp );
619 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
621 vtl->bg_line_thickness = data.u;
622 trw_layer_new_track_gcs ( vtl, vp );
627 /* Convert to store internally
628 NB file operation always in internal units (metres per second) */
629 vik_units_speed_t speed_units = a_vik_get_units_speed ();
630 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
631 vtl->velocity_min = data.d;
632 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
633 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
634 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
635 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
638 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
643 /* Convert to store internally
644 NB file operation always in internal units (metres per second) */
645 vik_units_speed_t speed_units = a_vik_get_units_speed ();
646 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
647 vtl->velocity_max = data.d;
648 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
649 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
650 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
651 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
654 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
657 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
658 case PARAM_DLA: vtl->drawlabels = data.b; break;
659 case PARAM_DI: vtl->drawimages = data.b; break;
660 case PARAM_IS: if ( data.u != vtl->image_size )
662 vtl->image_size = data.u;
663 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
664 g_queue_free ( vtl->image_cache );
665 vtl->image_cache = g_queue_new ();
668 case PARAM_IA: vtl->image_alpha = data.u; break;
669 case PARAM_ICS: vtl->image_cache_size = data.u;
670 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
671 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
673 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
674 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
675 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
676 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
677 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
678 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
679 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
684 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
686 VikLayerParamData rv;
689 case PARAM_TV: rv.b = vtl->tracks_visible; break;
690 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
691 case PARAM_DM: rv.u = vtl->drawmode; break;
692 case PARAM_DP: rv.b = vtl->drawpoints; break;
693 case PARAM_DE: rv.b = vtl->drawelevation; break;
694 case PARAM_EF: rv.u = vtl->elevation_factor; break;
695 case PARAM_DS: rv.b = vtl->drawstops; break;
696 case PARAM_SL: rv.u = vtl->stop_length; break;
697 case PARAM_DL: rv.b = vtl->drawlines; break;
698 case PARAM_LT: rv.u = vtl->line_thickness; break;
699 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
702 /* Convert to store internally
703 NB file operation always in internal units (metres per second) */
704 vik_units_speed_t speed_units = a_vik_get_units_speed ();
705 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
706 rv.d = vtl->velocity_min;
707 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
708 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
709 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
710 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
713 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
718 /* Convert to store internally
719 NB file operation always in internal units (metres per second) */
720 vik_units_speed_t speed_units = a_vik_get_units_speed ();
721 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
722 rv.d = vtl->velocity_max;
723 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
724 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
725 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
726 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
729 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
732 case PARAM_DLA: rv.b = vtl->drawlabels; break;
733 case PARAM_DI: rv.b = vtl->drawimages; break;
734 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
735 case PARAM_IS: rv.u = vtl->image_size; break;
736 case PARAM_IA: rv.u = vtl->image_alpha; break;
737 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
738 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
739 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
740 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
741 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
742 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
743 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
744 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
749 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
760 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
761 a_gpx_write_file(vtl, f);
762 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
765 g_file_get_contents(tmpname, &dd, &dl, NULL);
766 *len = sizeof(pl) + pl + dl;
767 *data = g_malloc(*len);
768 memcpy(*data, &pl, sizeof(pl));
769 memcpy(*data + sizeof(pl), pd, pl);
770 memcpy(*data + sizeof(pl) + pl, dd, dl);
779 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
781 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
787 memcpy(&pl, data, sizeof(pl));
789 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
792 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
793 g_critical("couldn't open temp file");
796 fwrite(data, len - pl - sizeof(pl), 1, f);
798 a_gpx_read_file(rv, f);
806 static GList * str_array_to_glist(gchar* data[])
810 for (p = (gpointer)data; *p; p++)
811 gl = g_list_prepend(gl, *p);
812 return(g_list_reverse(gl));
815 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
817 return (strcasecmp(s1, s2) == 0);
820 static guint strcase_hash(gconstpointer v)
822 /* 31 bit hash function */
825 gchar s[128]; /* malloc is too slow for reading big files */
828 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
829 p[i] = toupper(t[i]);
835 for (p += 1; *p != '\0'; p++)
836 h = (h << 5) - h + *p;
842 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
844 if (trw_layer_params[PARAM_DM].widget_data == NULL)
845 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
846 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
847 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
849 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
850 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
852 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
853 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
854 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
855 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
857 /* TODO: constants at top */
858 rv->waypoints_visible = rv->tracks_visible = TRUE;
859 rv->drawmode = drawmode;
860 rv->drawpoints = TRUE;
861 rv->drawstops = FALSE;
862 rv->drawelevation = FALSE;
863 rv->elevation_factor = 30;
864 rv->stop_length = 60;
865 rv->drawlines = TRUE;
866 rv->wplabellayout = NULL;
867 rv->wp_right_click_menu = NULL;
868 rv->waypoint_gc = NULL;
869 rv->waypoint_text_gc = NULL;
870 rv->waypoint_bg_gc = NULL;
872 rv->velocity_max = 5.0;
873 rv->velocity_min = 0.0;
874 rv->line_thickness = 1;
875 rv->bg_line_thickness = 0;
876 rv->current_wp = NULL;
877 rv->current_wp_name = NULL;
878 rv->current_track = NULL;
879 rv->current_tpl = NULL;
880 rv->current_tp_track_name = NULL;
881 rv->moving_tp = FALSE;
882 rv->moving_wp = FALSE;
884 rv->ct_sync_done = TRUE;
886 rv->magic_scissors_started = FALSE;
887 rv->magic_scissors_check_added_track = FALSE;
888 rv->magic_scissors_added_track_name = NULL;
889 rv->magic_scissors_current_track = NULL;
890 rv->magic_scissors_append = FALSE;
892 rv->waypoint_rightclick = FALSE;
894 rv->last_tp_track_name = NULL;
896 rv->image_cache = g_queue_new();
898 rv->image_alpha = 255;
899 rv->image_cache_size = 300;
900 rv->drawimages = TRUE;
901 rv->drawlabels = TRUE;
906 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
908 g_hash_table_destroy(trwlayer->waypoints);
909 g_hash_table_destroy(trwlayer->tracks);
911 /* ODC: replace with GArray */
912 trw_layer_free_track_gcs ( trwlayer );
914 if ( trwlayer->wp_right_click_menu )
915 gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) );
917 if ( trwlayer->wplabellayout != NULL)
918 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
920 if ( trwlayer->waypoint_gc != NULL )
921 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
923 if ( trwlayer->waypoint_text_gc != NULL )
924 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
926 if ( trwlayer->waypoint_bg_gc != NULL )
927 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
929 if ( trwlayer->waypoint_font != NULL )
930 gdk_font_unref ( trwlayer->waypoint_font );
932 if ( trwlayer->tpwin != NULL )
933 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
935 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
936 g_queue_free ( trwlayer->image_cache );
939 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
942 dp->xmpp = vik_viewport_get_xmpp ( vp );
943 dp->ympp = vik_viewport_get_ympp ( vp );
944 dp->width = vik_viewport_get_width ( vp );
945 dp->height = vik_viewport_get_height ( vp );
946 dp->center = vik_viewport_get_center ( vp );
947 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
948 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
953 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
954 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
955 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
957 dp->ce1 = dp->center->east_west-w2;
958 dp->ce2 = dp->center->east_west+w2;
959 dp->cn1 = dp->center->north_south-h2;
960 dp->cn2 = dp->center->north_south+h2;
961 } else if ( dp->lat_lon ) {
962 VikCoord upperleft, bottomright;
963 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
964 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
965 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
966 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
967 dp->ce1 = upperleft.east_west;
968 dp->ce2 = bottomright.east_west;
969 dp->cn1 = bottomright.north_south;
970 dp->cn2 = upperleft.north_south;
973 dp->track_gc_iter = 0;
976 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
978 static gdouble rv = 0;
979 if ( tp1->has_timestamp && tp2->has_timestamp )
981 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
982 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
985 return VIK_TRW_LAYER_TRACK_GC_MIN;
986 else if ( vtl->velocity_min >= vtl->velocity_max )
987 return VIK_TRW_LAYER_TRACK_GC_MAX;
989 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
991 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
992 return VIK_TRW_LAYER_TRACK_GC_MAX;
996 return VIK_TRW_LAYER_TRACK_GC_BLACK;
999 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1001 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1002 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1003 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1004 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1007 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1009 /* TODO: this function is a mess, get rid of any redundancy */
1010 GList *list = track->trackpoints;
1012 gboolean useoldvals = TRUE;
1014 gboolean drawpoints;
1016 gboolean drawelevation;
1017 gdouble min_alt, max_alt, alt_diff = 0;
1019 const guint8 tp_size_reg = 2;
1020 const guint8 tp_size_cur = 4;
1023 if ( dp->vtl->drawelevation )
1025 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1026 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1027 alt_diff = max_alt - min_alt;
1030 if ( ! track->visible )
1033 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1034 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1035 trw_layer_draw_track ( name, track, dp, TRUE );
1037 if ( drawing_white_background )
1038 drawpoints = drawstops = FALSE;
1040 drawpoints = dp->vtl->drawpoints;
1041 drawstops = dp->vtl->drawstops;
1044 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1045 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1047 if ( track == dp->vtl->current_track )
1048 main_gc = dp->vtl->current_track_gc;
1050 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1053 int x, y, oldx, oldy;
1054 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1056 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1058 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1060 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1062 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1063 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1069 while ((list = g_list_next(list)))
1071 tp = VIK_TRACKPOINT(list->data);
1072 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1074 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1075 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1076 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1077 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1078 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1080 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1082 if ( drawpoints && ! drawing_white_background )
1085 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1088 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1089 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 );
1092 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 );
1095 if ((!tp->newsegment) && (dp->vtl->drawlines))
1097 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1099 /* UTM only: zone check */
1100 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1101 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1103 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1104 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1105 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1109 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1111 if ( drawing_white_background ) {
1112 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1116 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1117 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1119 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1120 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1124 tmp[1].y = oldy-FIXALTITUDE(list->data);
1126 tmp[2].y = y-FIXALTITUDE(list->next->data);
1131 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1132 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1134 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1135 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1137 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1147 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1149 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1150 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1152 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1153 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1154 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1155 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1158 if ( drawing_white_background )
1159 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1161 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1165 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1166 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1173 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1174 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1175 dp->track_gc_iter = 0;
1178 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1179 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1181 trw_layer_draw_track ( name, track, dp, FALSE );
1184 static void cached_pixbuf_free ( CachedPixbuf *cp )
1186 g_object_unref ( G_OBJECT(cp->pixbuf) );
1187 g_free ( cp->image );
1190 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1192 return strcmp ( cp->image, name );
1195 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1198 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1199 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1200 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1203 GdkPixbuf *sym = NULL;
1204 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1206 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1208 if ( wp->image && dp->vtl->drawimages )
1210 GdkPixbuf *pixbuf = NULL;
1213 if ( dp->vtl->image_alpha == 0)
1216 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1218 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1221 gchar *image = wp->image;
1222 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1223 if ( ! regularthumb )
1225 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1226 image = "\x12\x00"; /* this shouldn't occur naturally. */
1230 CachedPixbuf *cp = NULL;
1231 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1232 if ( dp->vtl->image_size == 128 )
1233 cp->pixbuf = regularthumb;
1236 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1237 g_assert ( cp->pixbuf );
1238 g_object_unref ( G_OBJECT(regularthumb) );
1240 cp->image = g_strdup ( image );
1242 /* needed so 'click picture' tool knows how big the pic is; we don't
1243 * store it in cp because they may have been freed already. */
1244 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1245 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1247 g_queue_push_head ( dp->vtl->image_cache, cp );
1248 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1249 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1251 pixbuf = cp->pixbuf;
1255 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1261 w = gdk_pixbuf_get_width ( pixbuf );
1262 h = gdk_pixbuf_get_height ( pixbuf );
1264 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1266 if ( dp->vtl->image_alpha == 255 )
1267 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1269 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1271 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1275 /* DRAW ACTUAL DOT */
1276 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1277 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 );
1279 else if ( wp == dp->vtl->current_wp ) {
1280 switch ( dp->vtl->wp_symbol ) {
1281 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;
1282 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;
1283 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;
1284 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 );
1285 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 switch ( dp->vtl->wp_symbol ) {
1290 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;
1291 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;
1292 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;
1293 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 );
1294 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;
1298 if ( dp->vtl->drawlabels )
1300 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1301 gint label_x, label_y;
1303 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1304 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1305 label_x = x - width/2;
1307 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1309 label_y = y - dp->vtl->wp_size - height - 2;
1311 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1312 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1317 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1319 static struct DrawingParams dp;
1320 g_assert ( l != NULL );
1322 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1325 if ( l->tracks_visible )
1326 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1328 if (l->waypoints_visible)
1329 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1332 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1335 if ( vtl->track_bg_gc )
1337 g_object_unref ( vtl->track_bg_gc );
1338 vtl->track_bg_gc = NULL;
1340 if ( vtl->current_track_gc )
1342 g_object_unref ( vtl->current_track_gc );
1343 vtl->current_track_gc = NULL;
1346 if ( ! vtl->track_gc )
1348 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1349 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1350 g_array_free ( vtl->track_gc, TRUE );
1351 vtl->track_gc = NULL;
1354 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1356 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1357 gint width = vtl->line_thickness;
1359 if ( vtl->track_gc )
1360 trw_layer_free_track_gcs ( vtl );
1362 if ( vtl->track_bg_gc )
1363 g_object_unref ( vtl->track_bg_gc );
1364 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1366 if ( vtl->current_track_gc )
1367 g_object_unref ( vtl->current_track_gc );
1368 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1369 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1371 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1373 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1375 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1376 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1377 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1378 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1379 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1380 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1381 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1382 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1383 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1384 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1386 gc[11] = vik_viewport_new_gc ( vp, "#ff0000", width ); /* above range */
1388 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1390 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1393 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1395 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1396 PangoFontDescription *pfd;
1397 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1398 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1399 pango_layout_set_font_description (rv->wplabellayout, pfd);
1400 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1401 pango_font_description_free (pfd);
1403 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1405 trw_layer_new_track_gcs ( rv, vp );
1407 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1408 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1409 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1410 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1412 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1414 rv->has_verified_thumbnails = FALSE;
1415 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1417 rv->wp_draw_symbols = TRUE;
1419 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1421 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1426 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] )
1428 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1430 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1431 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 );
1433 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 );
1436 *new_iter = *((GtkTreeIter *) pass_along[1]);
1437 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1439 if ( ! track->visible )
1440 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1443 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] )
1445 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1446 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1447 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 );
1449 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 );
1452 *new_iter = *((GtkTreeIter *) pass_along[1]);
1453 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1455 if ( ! wp->visible )
1456 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1460 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1463 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1465 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1466 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1468 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1470 if ( ! vtl->tracks_visible )
1471 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1473 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1475 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1476 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1478 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1481 if ( ! vtl->waypoints_visible )
1482 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1484 pass_along[0] = &(vtl->waypoints_iter);
1485 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1487 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1491 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1495 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1496 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1497 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1499 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1501 return (t->visible ^= 1);
1505 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1507 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1509 return (t->visible ^= 1);
1517 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1522 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1524 return l->waypoints;
1527 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1529 static VikCoord fixme;
1530 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1531 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1532 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1533 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1534 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1535 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1536 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1537 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1538 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1541 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1544 static VikCoord fixme;
1548 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1549 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1550 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1551 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1552 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1553 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1554 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1555 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1556 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1561 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1563 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1564 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1566 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1567 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1568 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1569 maxmin[0].lat = wpt_maxmin[0].lat;
1572 maxmin[0].lat = trk_maxmin[0].lat;
1574 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1575 maxmin[0].lon = wpt_maxmin[0].lon;
1578 maxmin[0].lon = trk_maxmin[0].lon;
1580 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1581 maxmin[1].lat = wpt_maxmin[1].lat;
1584 maxmin[1].lat = trk_maxmin[1].lat;
1586 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1587 maxmin[1].lon = wpt_maxmin[1].lon;
1590 maxmin[1].lon = trk_maxmin[1].lon;
1594 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1596 /* 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... */
1597 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1598 trw_layer_find_maxmin (vtl, maxmin);
1599 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1603 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1604 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1609 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1612 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1613 goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord );
1615 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1618 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1620 /* First set the center [in case previously viewing from elsewhere] */
1621 /* Then loop through zoom levels until provided positions are in view */
1622 /* This method is not particularly fast - but should work well enough */
1623 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1625 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1626 vik_viewport_set_center_coord ( vvp, &coord );
1628 /* Convert into definite 'smallest' and 'largest' positions */
1629 struct LatLon minmin;
1630 if ( maxmin[0].lat < maxmin[1].lat )
1631 minmin.lat = maxmin[0].lat;
1633 minmin.lat = maxmin[1].lat;
1635 struct LatLon maxmax;
1636 if ( maxmin[0].lon > maxmin[1].lon )
1637 maxmax.lon = maxmin[0].lon;
1639 maxmax.lon = maxmin[1].lon;
1641 /* Never zoom in too far - generally not that useful, as too close ! */
1642 /* Always recalculate the 'best' zoom level */
1644 vik_viewport_set_zoom ( vvp, zoom );
1646 gdouble min_lat, max_lat, min_lon, max_lon;
1647 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1648 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1649 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1650 /* NB I think the logic used in this test to determine if the bounds is within view
1651 fails if track goes across 180 degrees longitude.
1652 Hopefully that situation is not too common...
1653 Mind you viking doesn't really do edge locations to well anyway */
1654 if ( min_lat < minmin.lat &&
1655 max_lat > minmin.lat &&
1656 min_lon < maxmax.lon &&
1657 max_lon > maxmax.lon )
1658 /* Found within zoom level */
1663 vik_viewport_set_zoom ( vvp, zoom );
1667 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
1669 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
1670 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1671 trw_layer_find_maxmin (vtl, maxmin);
1672 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1675 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
1680 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
1682 GtkWidget *file_selector;
1684 gboolean failed = FALSE;
1685 file_selector = gtk_file_chooser_dialog_new (title,
1687 GTK_FILE_CHOOSER_ACTION_SAVE,
1688 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1689 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1691 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
1693 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
1695 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
1696 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
1698 gtk_widget_hide ( file_selector );
1699 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
1704 if ( a_dialog_overwrite ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
1706 gtk_widget_hide ( file_selector );
1707 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
1712 gtk_widget_destroy ( file_selector );
1714 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
1717 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
1719 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
1722 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
1724 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
1727 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
1729 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX );
1732 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
1734 gpointer layer_and_vlp[2];
1735 layer_and_vlp[0] = pass_along[0];
1736 layer_and_vlp[1] = pass_along[1];
1738 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
1739 gchar *auto_save_name = g_strdup ( pass_along[3] );
1740 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
1741 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
1743 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
1745 g_free ( auto_save_name );
1748 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
1750 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
1751 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
1752 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
1753 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1755 GTK_RESPONSE_REJECT,
1757 GTK_RESPONSE_ACCEPT,
1760 GtkWidget *label, *entry;
1761 label = gtk_label_new(_("Waypoint Name:"));
1762 entry = gtk_entry_new();
1764 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
1765 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
1766 gtk_widget_show_all ( label );
1767 gtk_widget_show_all ( entry );
1769 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
1771 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
1774 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
1777 for ( i = strlen(upname)-1; i >= 0; i-- )
1778 upname[i] = toupper(upname[i]);
1780 wp = g_hash_table_lookup ( wps, upname );
1783 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
1786 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
1787 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1788 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 ) );
1795 gtk_widget_destroy ( dia );
1798 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
1800 gchar *name = highest_wp_number_get(vtl);
1801 VikWaypoint *wp = vik_waypoint_new();
1802 wp->coord = *def_coord;
1804 if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) )
1807 vik_trw_layer_add_waypoint ( vtl, name, wp );
1810 vik_waypoint_free(wp);
1814 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
1817 struct LatLon one_ll, two_ll;
1818 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1820 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1821 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1822 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
1823 VikViewport *vvp = vik_window_viewport(vw);
1824 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
1825 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
1826 vik_coord_to_latlon(&one, &one_ll);
1827 vik_coord_to_latlon(&two, &two_ll);
1828 if (one_ll.lat > two_ll.lat) {
1829 maxmin[0].lat = one_ll.lat;
1830 maxmin[1].lat = two_ll.lat;
1833 maxmin[0].lat = two_ll.lat;
1834 maxmin[1].lat = one_ll.lat;
1836 if (one_ll.lon > two_ll.lon) {
1837 maxmin[0].lon = one_ll.lon;
1838 maxmin[1].lon = two_ll.lon;
1841 maxmin[0].lon = two_ll.lon;
1842 maxmin[1].lon = one_ll.lon;
1844 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
1847 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
1849 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1850 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1851 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1853 trw_layer_find_maxmin (vtl, maxmin);
1854 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
1857 static void trw_layer_new_wp ( gpointer lav[2] )
1859 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
1860 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
1861 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
1862 instead return true if you want to update. */
1863 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 )
1864 vik_layers_panel_emit_update ( vlp );
1867 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
1869 static gpointer pass_along[2];
1871 GtkWidget *export_submenu;
1872 GtkWidget *wikipedia_submenu;
1873 pass_along[0] = vtl;
1874 pass_along[1] = vlp;
1876 item = gtk_menu_item_new();
1877 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
1878 gtk_widget_show ( item );
1880 item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
1881 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
1882 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1883 gtk_widget_show ( item );
1885 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint") );
1886 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
1887 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1888 gtk_widget_show ( item );
1890 export_submenu = gtk_menu_new ();
1891 item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
1892 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1893 gtk_widget_show ( item );
1894 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1896 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point") );
1897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
1898 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1899 gtk_widget_show ( item );
1901 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper") );
1902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1903 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1904 gtk_widget_show ( item );
1906 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX") );
1907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1908 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
1909 gtk_widget_show ( item );
1911 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
1912 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
1913 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1914 gtk_widget_show ( item );
1916 #ifdef VIK_CONFIG_GEONAMES
1917 wikipedia_submenu = gtk_menu_new();
1918 item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
1919 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
1920 gtk_widget_show(item);
1921 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
1923 item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
1924 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
1925 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
1926 gtk_widget_show ( item );
1928 item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
1929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
1930 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
1931 gtk_widget_show ( item );
1934 #ifdef VIK_CONFIG_OPENSTREETMAP
1935 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
1936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
1937 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1938 gtk_widget_show ( item );
1941 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
1942 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
1944 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1945 gtk_widget_show ( item );
1948 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
1949 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
1951 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1952 gtk_widget_show ( item );
1956 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
1958 if ( VIK_LAYER(vtl)->realized )
1960 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
1962 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
1965 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
1966 // Visibility column always needed for waypoints
1967 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1968 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
1970 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
1972 // Actual setting of visibility dependent on the waypoint
1973 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
1974 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
1975 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
1979 highest_wp_number_add_wp(vtl, name);
1980 g_hash_table_insert ( vtl->waypoints, name, wp );
1984 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
1986 if ( VIK_LAYER(vtl)->realized )
1988 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
1990 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
1993 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
1994 // Visibility column always needed for tracks
1995 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1996 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
1998 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2000 // Actual setting of visibility dependent on the track
2001 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2002 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2003 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2007 g_hash_table_insert ( vtl->tracks, name, t );
2011 /* to be called whenever a track has been deleted or may have been changed. */
2012 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2014 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2015 trw_layer_cancel_current_tp ( vtl, FALSE );
2016 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2017 trw_layer_cancel_last_tp ( vtl );
2020 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2023 gchar *newname = g_strdup(name);
2024 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2025 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2026 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2028 newname = new_newname;
2034 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2036 vik_trw_layer_add_waypoint ( vtl,
2037 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2040 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2042 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
2043 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2044 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2045 vik_track_free ( tr );
2046 vtl->magic_scissors_append = FALSE; /* this means we have added it */
2048 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2049 vik_trw_layer_add_track ( vtl, new_name, tr );
2051 if ( vtl->magic_scissors_check_added_track ) {
2052 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2053 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2054 g_free ( vtl->magic_scissors_added_track_name );
2055 vtl->magic_scissors_added_track_name = g_strdup(new_name);
2060 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2062 *l = g_list_append(*l, (gpointer)name);
2065 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2067 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2068 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2070 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2071 vik_trw_layer_delete_track(vtl_src, name);
2072 vik_trw_layer_add_track(vtl_dest, newname, t);
2074 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2076 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2077 vik_trw_layer_delete_waypoint(vtl_src, name);
2078 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2082 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2084 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2085 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2087 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2088 GList *items = NULL;
2091 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2092 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2094 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2095 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2100 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2101 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2103 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2110 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2111 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2116 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2118 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2119 gboolean was_visible = FALSE;
2123 was_visible = t->visible;
2124 if ( t == vtl->current_track )
2125 vtl->current_track = NULL;
2126 if ( t == vtl->magic_scissors_current_track )
2127 vtl->magic_scissors_current_track = NULL;
2129 /* could be current_tp, so we have to check */
2130 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2132 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2133 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2134 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2136 /* do this last because trk_name may be pointing to actual orig key */
2137 g_hash_table_remove ( vtl->tracks, trk_name );
2142 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2144 gboolean was_visible = FALSE;
2147 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2151 if ( wp == vtl->current_wp ) {
2152 vtl->current_wp = NULL;
2153 vtl->current_wp_name = NULL;
2154 vtl->moving_wp = FALSE;
2157 was_visible = wp->visible;
2158 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2159 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2160 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2162 highest_wp_number_remove_wp(vtl, wp_name);
2163 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2169 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2171 vik_treeview_item_delete (vt, it );
2174 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2177 vtl->current_track = NULL;
2178 vtl->magic_scissors_current_track = NULL;
2179 if (vtl->current_tp_track_name)
2180 trw_layer_cancel_current_tp(vtl, FALSE);
2181 if (vtl->last_tp_track_name)
2182 trw_layer_cancel_last_tp ( vtl );
2184 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2185 g_hash_table_remove_all(vtl->tracks_iters);
2186 g_hash_table_remove_all(vtl->tracks);
2188 /* TODO: only update if the layer is visible (ticked) */
2189 vik_layer_emit_update ( VIK_LAYER(vtl) );
2192 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2194 vtl->current_wp = NULL;
2195 vtl->current_wp_name = NULL;
2196 vtl->moving_wp = FALSE;
2198 highest_wp_number_reset(vtl);
2200 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2201 g_hash_table_remove_all(vtl->waypoints_iters);
2202 g_hash_table_remove_all(vtl->waypoints);
2204 /* TODO: only update if the layer is visible (ticked) */
2205 vik_layer_emit_update ( VIK_LAYER(vtl) );
2208 static void trw_layer_delete_item ( gpointer pass_along[5] )
2210 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2211 gboolean was_visible = FALSE;
2212 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2214 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2218 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2221 vik_layer_emit_update ( VIK_LAYER(vtl) );
2225 static void trw_layer_properties_item ( gpointer pass_along[5] )
2227 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2228 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2230 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2233 if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) )
2235 if ( VIK_LAYER(vtl)->visible )
2236 vik_layer_emit_update ( VIK_LAYER(vtl) );
2241 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2244 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2246 pass_along[1], /* vlp */
2247 pass_along[3] /* track name */);
2252 static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord )
2254 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord );
2255 vik_layers_panel_emit_update ( vlp );
2258 static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] )
2260 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2261 if ( trps && trps->data )
2262 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2265 static void trw_layer_goto_track_center ( gpointer pass_along[5] )
2267 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2268 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2269 if ( trps && *trps )
2271 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2273 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2274 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2275 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2276 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2277 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord);
2281 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2283 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2284 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2286 vtl->current_track = track;
2287 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2289 if ( track->trackpoints )
2290 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2294 * extend a track using magic scissors
2296 static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2298 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2299 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2300 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2302 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2303 vtl->magic_scissors_coord = last_coord;
2304 vtl->magic_scissors_current_track = track;
2305 vtl->magic_scissors_started = TRUE;
2307 if ( track->trackpoints )
2308 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &last_coord) ;
2312 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2314 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2315 /* Also warn if overwrite old elevation data */
2316 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2318 vik_track_apply_dem_data ( track );
2321 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2323 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2326 trps = g_list_last(trps);
2327 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2330 static void trw_layer_goto_track_max_speed ( gpointer pass_along[5] )
2332 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2335 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2338 static void trw_layer_goto_track_max_alt ( gpointer pass_along[5] )
2340 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2343 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2346 static void trw_layer_goto_track_min_alt ( gpointer pass_along[5] )
2348 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2351 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2354 /*************************************
2355 * merge/split by time routines
2356 *************************************/
2358 /* called for each key in track hash table.
2359 * If the current track has time stamp, add it to the result,
2360 * except the one pointed by "exclude".
2361 * set exclude to NULL if there is no exclude to check.
2362 * Not that result is in reverse (for performance reason).
2368 static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2370 twt_udata *user_data = udata;
2371 VikTrackpoint *p1, *p2;
2373 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2377 if (VIK_TRACK(value)->trackpoints) {
2378 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2379 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2381 if (!p1->has_timestamp || !p2->has_timestamp) {
2382 g_print("no timestamp\n");
2388 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2391 /* called for each key in track hash table. if original track user_data[1] is close enough
2392 * to the passed one, add it to list in user_data[0]
2394 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2397 VikTrackpoint *p1, *p2;
2399 GList **nearby_tracks = ((gpointer *)user_data)[0];
2400 GList *orig_track = ((gpointer *)user_data)[1];
2401 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
2404 * detect reasons for not merging, and return
2405 * if no reason is found not to merge, then do it.
2408 if (VIK_TRACK(value)->trackpoints == orig_track) {
2412 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2413 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2415 if (VIK_TRACK(value)->trackpoints) {
2416 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2417 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2419 if (!p1->has_timestamp || !p2->has_timestamp) {
2420 g_print("no timestamp\n");
2424 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2425 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2427 abs(p1->timestamp - t2) < thr*60)
2434 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2437 /* comparison function used to sort tracks; a and b are hash table keys */
2438 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2440 GHashTable *tracks = user_data;
2443 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2444 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2446 if (t1 < t2) return -1;
2447 if (t1 > t2) return 1;
2451 /* comparison function used to sort trackpoints */
2452 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2454 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2456 if (t1 < t2) return -1;
2457 if (t1 > t2) return 1;
2461 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2463 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2464 gchar *orig_track_name = pass_along[3];
2465 GList *tracks_with_timestamp = NULL;
2466 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2468 if (track->trackpoints &&
2469 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2470 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2477 udata.result = &tracks_with_timestamp;
2478 udata.exclude = track->trackpoints;
2479 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2480 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2483 if (!tracks_with_timestamp) {
2484 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2488 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2489 vtl->tracks, tracks_with_timestamp, TRUE,
2490 _("Merge with..."), _("Select track to merge with"));
2491 g_list_free(tracks_with_timestamp);
2496 for (l = merge_list; l != NULL; l = g_list_next(l)) {
2497 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2499 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2500 merge_track->trackpoints = NULL;
2501 vik_trw_layer_delete_track(vtl, l->data);
2502 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2505 /* TODO: free data before free merge_list */
2506 for (l = merge_list; l != NULL; l = g_list_next(l))
2508 g_list_free(merge_list);
2509 vik_layer_emit_update( VIK_LAYER(vtl) );
2513 /* merge by time routine */
2514 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2516 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2517 gchar *orig_track_name = strdup(pass_along[3]);
2520 GList *nearby_tracks;
2523 static guint thr = 1;
2524 guint track_count = 0;
2526 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2527 _("Merge Threshold..."),
2528 _("Merge when time between tracks less than:"),
2530 free(orig_track_name);
2534 /* merge tracks until we can't */
2535 nearby_tracks = NULL;
2539 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2540 trps = track->trackpoints;
2545 if (nearby_tracks) {
2546 g_list_free(nearby_tracks);
2547 nearby_tracks = NULL;
2550 t1 = ((VikTrackpoint *)trps->data)->timestamp;
2551 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
2553 /* g_print("Original track times: %d and %d\n", t1, t2); */
2554 params[0] = &nearby_tracks;
2556 params[2] = GUINT_TO_POINTER (thr);
2558 /* get a list of adjacent-in-time tracks */
2559 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
2561 /* add original track */
2562 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
2566 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
2567 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
2568 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
2569 GList *l = nearby_tracks;
2570 VikTrack *tr = vik_track_new();
2571 tr->visible = track->visible;
2576 t1 = get_first_trackpoint(l)->timestamp;
2577 t2 = get_last_trackpoint(l)->timestamp;
2578 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
2582 /* remove trackpoints from merged track, delete track */
2583 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
2584 get_track(l)->trackpoints = NULL;
2585 vik_trw_layer_delete_track(vtl, l->data);
2590 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
2591 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
2593 #undef get_first_trackpoint
2594 #undef get_last_trackpoint
2597 } while (track_count > 1);
2598 g_list_free(nearby_tracks);
2599 free(orig_track_name);
2600 vik_layer_emit_update( VIK_LAYER(vtl) );
2603 /* split by time routine */
2604 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
2606 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2607 GList *trps = track->trackpoints;
2609 GList *newlists = NULL;
2610 GList *newtps = NULL;
2612 static guint thr = 1;
2619 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2620 _("Split Threshold..."),
2621 _("Split when time between trackpoints exceeds:"),
2626 /* iterate through trackpoints, and copy them into new lists without touching original list */
2627 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
2631 ts = VIK_TRACKPOINT(iter->data)->timestamp;
2633 g_print("panic: ts < prev_ts: this should never happen!\n");
2636 if (ts - prev_ts > thr*60) {
2637 /* flush accumulated trackpoints into new list */
2638 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2642 /* accumulate trackpoint copies in newtps, in reverse order */
2643 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
2645 iter = g_list_next(iter);
2648 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2651 /* put lists of trackpoints into tracks */
2658 tr = vik_track_new();
2659 tr->visible = track->visible;
2660 tr->trackpoints = (GList *)(iter->data);
2662 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
2663 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
2664 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
2665 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
2667 iter = g_list_next(iter);
2669 g_list_free(newlists);
2670 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
2671 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2674 /* end of split/merge routines */
2677 static void trw_layer_goto_waypoint ( gpointer pass_along[5] )
2679 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2681 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) );
2684 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] )
2686 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
2687 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
2691 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2693 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2698 if (strcmp(newname, sublayer) == 0 )
2701 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
2702 if (g_hash_table_lookup( l->waypoints, newname))
2704 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
2709 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
2710 g_hash_table_steal ( l->waypoints_iters, sublayer );
2712 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
2713 highest_wp_number_remove_wp(l, sublayer);
2714 g_hash_table_remove ( l->waypoints, sublayer );
2716 rv = g_strdup(newname);
2718 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2720 highest_wp_number_add_wp(l, rv);
2721 g_hash_table_insert ( l->waypoints, rv, wp );
2722 g_hash_table_insert ( l->waypoints_iters, rv, iter );
2724 /* it hasn't been updated yet so we pass new name */
2725 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2726 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2729 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2732 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2739 if (strcmp(newname, sublayer) == 0)
2742 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
2743 if (g_hash_table_lookup( l->tracks, newname))
2745 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
2750 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
2751 g_hash_table_steal ( l->tracks, sublayer );
2753 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
2754 g_hash_table_steal ( l->tracks_iters, sublayer );
2756 rv = g_strdup(newname);
2758 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
2760 g_hash_table_insert ( l->tracks, rv, tr );
2761 g_hash_table_insert ( l->tracks_iters, rv, iter );
2763 /* don't forget about current_tp_track_name, update that too */
2764 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
2766 l->current_tp_track_name = rv;
2768 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
2770 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
2771 l->last_tp_track_name = rv;
2773 g_free ( orig_key );
2775 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2776 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
2779 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2785 static gboolean is_valid_geocache_name ( gchar *str )
2787 gint len = strlen ( str );
2788 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]));
2791 static void trw_layer_track_use_with_filter ( gpointer *pass_along )
2793 gchar *track_name = (gchar *) pass_along[3];
2794 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
2795 a_acquire_set_filter_track ( tr, track_name );
2798 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
2800 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
2801 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
2804 static void trw_layer_track_google_route_webpage( gpointer *pass_along )
2806 gchar *track_name = (gchar *) pass_along[3];
2807 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
2809 gchar *escaped = uri_escape ( tr->comment );
2810 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
2811 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
2817 /* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */
2818 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
2820 static GtkTreeIter staticiter;
2821 static gpointer pass_along[5];
2823 gboolean rv = FALSE;
2826 pass_along[1] = vlp;
2827 pass_along[2] = GINT_TO_POINTER (subtype);
2828 pass_along[3] = sublayer;
2829 staticiter = *iter; /* will exist after function has ended */
2830 pass_along[4] = &staticiter;
2832 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2836 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
2837 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
2838 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2839 gtk_widget_show ( item );
2841 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2842 VikTrwLayer *vtl = l;
2843 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
2844 if (tr && tr->property_dialog)
2845 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
2848 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
2849 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
2850 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2851 gtk_widget_show ( item );
2853 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
2854 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
2855 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2856 gtk_widget_show ( item );
2858 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
2859 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
2860 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2861 gtk_widget_show ( item );
2863 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2865 /* could be a right-click using the tool */
2866 if ( vlp != NULL ) {
2867 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
2868 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
2869 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2870 gtk_widget_show ( item );
2873 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
2875 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
2876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
2877 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2878 gtk_widget_show ( item );
2884 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2886 GtkWidget *goto_submenu;
2887 item = gtk_menu_item_new ();
2888 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2889 gtk_widget_show ( item );
2891 goto_submenu = gtk_menu_new ();
2892 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
2893 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2894 gtk_widget_show ( item );
2895 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
2897 item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
2898 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
2899 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2900 gtk_widget_show ( item );
2902 item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
2903 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
2904 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2905 gtk_widget_show ( item );
2907 item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
2908 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
2909 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2910 gtk_widget_show ( item );
2912 item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
2913 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
2914 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2915 gtk_widget_show ( item );
2917 item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
2918 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
2919 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2920 gtk_widget_show ( item );
2922 item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
2923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
2924 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
2925 gtk_widget_show ( item );
2927 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time") );
2928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
2929 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2930 gtk_widget_show ( item );
2932 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
2933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
2934 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2935 gtk_widget_show ( item );
2937 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time") );
2938 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
2939 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2940 gtk_widget_show ( item );
2942 item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
2943 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
2944 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2945 gtk_widget_show ( item );
2947 item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
2948 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
2949 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2950 gtk_widget_show ( item );
2952 item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX") );
2953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
2954 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2955 gtk_widget_show ( item );
2957 item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
2958 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
2959 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2960 gtk_widget_show ( item );
2962 item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Magic Scissors") );
2963 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
2964 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2965 gtk_widget_show ( item );
2967 #ifdef VIK_CONFIG_OPENSTREETMAP
2968 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
2969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
2970 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2971 gtk_widget_show ( item );
2974 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
2976 item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
2977 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
2978 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2979 gtk_widget_show ( item );
2982 item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
2983 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
2984 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2985 gtk_widget_show ( item );
2987 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
2988 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
2989 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
2991 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2992 gtk_widget_show ( item );
2996 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
2998 item = gtk_menu_item_new ();
2999 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3000 gtk_widget_show ( item );
3002 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
3003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3004 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3005 gtk_widget_show ( item );
3011 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3014 if (!vtl->current_tpl)
3016 if (!vtl->current_tpl->next)
3019 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3020 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3022 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3025 VikTrackpoint *tp_new = vik_trackpoint_new();
3026 struct LatLon ll_current, ll_next;
3027 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3028 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3030 /* main positional interpolation */
3031 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3032 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3034 /* Now other properties that can be interpolated */
3035 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3037 if (tp_current->has_timestamp && tp_next->has_timestamp) {
3038 /* Note here the division is applied to each part, then added
3039 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3040 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3041 tp_new->has_timestamp = TRUE;
3044 if (tp_current->speed != NAN && tp_next->speed != NAN)
3045 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3047 /* TODO - improve interpolation of course, as it may not be correct.
3048 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3049 [similar applies if value is in radians] */
3050 if (tp_current->course != NAN && tp_next->course != NAN)
3051 tp_new->speed = (tp_current->course + tp_next->course)/2;
3053 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3055 /* Insert new point into the trackpoints list after the current TP */
3056 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3057 gint index = g_list_index ( tr->trackpoints, tp_current );
3059 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3064 /* to be called when last_tpl no long exists. */
3065 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3067 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3068 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3069 vtl->last_tpl = NULL;
3070 vtl->last_tp_track_name = NULL;
3073 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3079 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3083 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3085 if ( vtl->current_tpl )
3087 vtl->current_tpl = NULL;
3088 vtl->current_tp_track_name = NULL;
3089 vik_layer_emit_update(VIK_LAYER(vtl));
3093 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3095 g_assert ( vtl->tpwin != NULL );
3096 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3097 trw_layer_cancel_current_tp ( vtl, TRUE );
3098 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3101 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, NULL ) ) )
3103 VikTrack *tr = vik_track_new ();
3104 GList *newglist = g_list_alloc ();
3105 newglist->prev = NULL;
3106 newglist->next = vtl->current_tpl->next;
3107 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3108 tr->trackpoints = newglist;
3110 vtl->current_tpl->next->prev = newglist; /* end old track here */
3111 vtl->current_tpl->next = NULL;
3113 vtl->current_tpl = newglist; /* change tp to first of new track. */
3114 vtl->current_tp_track_name = name;
3116 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3120 vik_trw_layer_add_track ( vtl, name, tr );
3121 vik_layer_emit_update(VIK_LAYER(vtl));
3124 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3126 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3128 g_assert(tr != NULL);
3130 /* can't join with a non-existent trackpoint */
3131 vtl->last_tpl = NULL;
3132 vtl->last_tp_track_name = NULL;
3134 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3136 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3137 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3139 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3141 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3142 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3144 trw_layer_cancel_last_tp ( vtl );
3146 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3147 g_list_free_1 ( vtl->current_tpl );
3148 vtl->current_tpl = new_tpl;
3149 vik_layer_emit_update(VIK_LAYER(vtl));
3153 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3154 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3155 g_list_free_1 ( vtl->current_tpl );
3156 trw_layer_cancel_current_tp ( vtl, FALSE );
3159 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3161 vtl->last_tpl = vtl->current_tpl;
3162 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3163 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3165 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3167 vtl->last_tpl = vtl->current_tpl;
3168 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3169 vik_layer_emit_update(VIK_LAYER(vtl));
3171 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
3173 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
3174 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3176 VikTrack *tr_first = tr1, *tr_last = tr2;
3180 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
3181 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
3182 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
3183 vik_track_reverse ( tr1 );
3184 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
3189 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
3191 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. */
3192 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
3193 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
3194 tr2->trackpoints = NULL;
3196 tmp = vtl->current_tp_track_name;
3198 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
3199 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3201 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
3202 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
3203 vik_trw_layer_delete_track ( vtl, tmp );
3205 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
3206 vik_layer_emit_update(VIK_LAYER(vtl));
3208 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
3210 trw_layer_insert_tp_after_current_tp ( vtl );
3211 vik_layer_emit_update(VIK_LAYER(vtl));
3213 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
3214 vik_layer_emit_update (VIK_LAYER(vtl));
3217 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
3221 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
3222 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
3223 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
3224 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
3225 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
3227 if ( vtl->current_tpl )
3228 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3229 /* set layer name and TP data */
3232 /***************************************************************************
3234 ***************************************************************************/
3236 /*** Utility data structures and functions ****/
3240 gint closest_x, closest_y;
3241 gchar *closest_wp_name;
3242 VikWaypoint *closest_wp;
3248 gint closest_x, closest_y;
3249 gchar *closest_track_name;
3250 VikTrackpoint *closest_tp;
3255 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
3261 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
3263 if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
3264 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
3265 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3267 params->closest_wp_name = name;
3268 params->closest_wp = wp;
3269 params->closest_x = x;
3270 params->closest_y = y;
3274 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
3276 GList *tpl = t->trackpoints;
3285 tp = VIK_TRACKPOINT(tpl->data);
3287 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
3289 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
3290 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
3291 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3293 params->closest_track_name = name;
3294 params->closest_tp = tp;
3295 params->closest_tpl = tpl;
3296 params->closest_x = x;
3297 params->closest_y = y;
3303 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3305 TPSearchParams params;
3309 params.closest_track_name = NULL;
3310 params.closest_tp = NULL;
3311 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3312 return params.closest_tp;
3315 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3317 WPSearchParams params;
3321 params.closest_wp = NULL;
3322 params.closest_wp_name = NULL;
3323 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3324 return params.closest_wp;
3327 /* background drawing hook, to be passed the viewport */
3328 static gboolean tool_sync_done = TRUE;
3330 static gboolean tool_sync(gpointer data)
3332 VikViewport *vvp = data;
3333 gdk_threads_enter();
3334 vik_viewport_sync(vvp);
3335 tool_sync_done = TRUE;
3336 gdk_threads_leave();
3347 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
3350 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
3351 gdk_gc_set_function ( t->gc, GDK_INVERT );
3352 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3353 vik_viewport_sync(t->vvp);
3358 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
3360 VikViewport *vvp = t->vvp;
3361 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3362 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3366 if (tool_sync_done) {
3367 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
3368 tool_sync_done = FALSE;
3372 static void marker_end_move ( tool_ed_t *t )
3374 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3375 g_object_unref ( t->gc );
3379 /*** Edit waypoint ****/
3381 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3383 tool_ed_t *t = g_new(tool_ed_t, 1);
3389 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3391 WPSearchParams params;
3392 tool_ed_t *t = data;
3393 VikViewport *vvp = t->vvp;
3395 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3402 if ( !vtl->vl.visible || !vtl->waypoints_visible )
3405 if ( vtl->current_wp && vtl->current_wp->visible )
3407 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
3409 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
3411 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
3412 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
3414 if ( event->button == 3 )
3415 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3417 marker_begin_move(t, event->x, event->y);
3424 params.x = event->x;
3425 params.y = event->y;
3426 params.closest_wp_name = NULL;
3427 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3428 params.closest_wp = NULL;
3429 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3430 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
3432 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
3433 marker_begin_move(t, event->x, event->y);
3434 g_critical("shouldn't be here");
3437 else if ( params.closest_wp )
3439 if ( event->button == 3 )
3440 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3442 vtl->waypoint_rightclick = FALSE;
3444 vtl->current_wp = params.closest_wp;
3445 vtl->current_wp_name = params.closest_wp_name;
3447 if ( params.closest_wp )
3448 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
3450 /* could make it so don't update if old WP is off screen and new is null but oh well */
3451 vik_layer_emit_update ( VIK_LAYER(vtl) );
3455 vtl->current_wp = NULL;
3456 vtl->current_wp_name = NULL;
3457 vtl->waypoint_rightclick = FALSE;
3458 vik_layer_emit_update ( VIK_LAYER(vtl) );
3462 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
3464 tool_ed_t *t = data;
3465 VikViewport *vvp = t->vvp;
3467 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3472 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3475 if ( event->state & GDK_CONTROL_MASK )
3477 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3479 new_coord = tp->coord;
3483 if ( event->state & GDK_SHIFT_MASK )
3485 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3486 if ( wp && wp != vtl->current_wp )
3487 new_coord = wp->coord;
3492 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3494 marker_moveto ( t, x, y );
3501 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3503 tool_ed_t *t = data;
3504 VikViewport *vvp = t->vvp;
3506 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3509 if ( t->holding && event->button == 1 )
3512 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3515 if ( event->state & GDK_CONTROL_MASK )
3517 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3519 new_coord = tp->coord;
3523 if ( event->state & GDK_SHIFT_MASK )
3525 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3526 if ( wp && wp != vtl->current_wp )
3527 new_coord = wp->coord;
3530 marker_end_move ( t );
3532 vtl->current_wp->coord = new_coord;
3533 vik_layer_emit_update ( VIK_LAYER(vtl) );
3536 /* PUT IN RIGHT PLACE!!! */
3537 if ( event->button == 3 && vtl->waypoint_rightclick )
3539 if ( vtl->wp_right_click_menu )
3540 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
3541 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
3542 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 ) );
3543 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
3544 vtl->waypoint_rightclick = FALSE;
3549 /**** Begin track ***/
3550 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
3555 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3557 vtl->current_track = NULL;
3558 return tool_new_track_click ( vtl, event, vvp );
3561 /*** New track ****/
3563 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
3572 } new_track_move_passalong_t;
3574 /* sync and undraw, but only when we have time */
3575 static gboolean ct_sync ( gpointer passalong )
3577 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
3578 vik_viewport_sync ( p->vvp );
3579 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
3580 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
3581 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
3582 p->vtl->ct_sync_done = TRUE;
3587 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
3589 /* if we haven't sync'ed yet, we don't have time to do more. */
3590 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
3591 GList *iter = vtl->current_track->trackpoints;
3592 new_track_move_passalong_t *passalong;
3595 while ( iter->next )
3597 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
3598 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
3599 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
3600 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
3602 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
3603 passalong->vtl = vtl;
3604 passalong->vvp = vvp;
3607 passalong->x2 = event->x;
3608 passalong->y2 = event->y;
3610 /* this will sync and undraw when we have time to */
3611 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
3612 vtl->ct_sync_done = FALSE;
3613 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
3615 return VIK_LAYER_TOOL_ACK;
3618 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
3620 if ( vtl->current_track && event->keyval == GDK_Escape ) {
3621 vtl->current_track = NULL;
3622 vik_layer_emit_update ( VIK_LAYER(vtl) );
3624 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
3626 if ( vtl->current_track->trackpoints )
3628 GList *last = g_list_last(vtl->current_track->trackpoints);
3629 g_free ( last->data );
3630 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3632 vik_layer_emit_update ( VIK_LAYER(vtl) );
3638 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3642 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3645 if ( event->button == 3 && vtl->current_track )
3648 if ( vtl->current_track->trackpoints )
3650 GList *last = g_list_last(vtl->current_track->trackpoints);
3651 g_free ( last->data );
3652 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3654 vik_layer_emit_update ( VIK_LAYER(vtl) );
3658 if ( event->type == GDK_2BUTTON_PRESS )
3660 /* subtract last (duplicate from double click) tp then end */
3661 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
3663 GList *last = g_list_last(vtl->current_track->trackpoints);
3664 g_free ( last->data );
3665 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3666 /* undo last, then end */
3667 vtl->current_track = NULL;
3669 vik_layer_emit_update ( VIK_LAYER(vtl) );
3673 if ( ! vtl->current_track )
3675 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
3676 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
3678 vtl->current_track = vik_track_new();
3679 vtl->current_track->visible = TRUE;
3680 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3682 /* incase it was created by begin track */
3683 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3688 tp = vik_trackpoint_new();
3689 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
3691 /* snap to other TP */
3692 if ( event->state & GDK_CONTROL_MASK )
3694 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3696 tp->coord = other_tp->coord;
3699 tp->newsegment = FALSE;
3700 tp->has_timestamp = FALSE;
3702 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
3704 vtl->ct_x1 = vtl->ct_x2;
3705 vtl->ct_y1 = vtl->ct_y2;
3706 vtl->ct_x2 = event->x;
3707 vtl->ct_y2 = event->y;
3709 vik_layer_emit_update ( VIK_LAYER(vtl) );
3714 /*** New waypoint ****/
3716 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3721 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3724 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3726 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
3727 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
3728 vik_layer_emit_update ( VIK_LAYER(vtl) );
3733 /*** Edit trackpoint ****/
3735 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
3737 tool_ed_t *t = g_new(tool_ed_t, 1);
3743 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3745 tool_ed_t *t = data;
3746 VikViewport *vvp = t->vvp;
3747 TPSearchParams params;
3748 /* OUTDATED DOCUMENTATION:
3749 find 5 pixel range on each side. then put these UTM, and a pointer
3750 to the winning track name (and maybe the winning track itself), and a
3751 pointer to the winning trackpoint, inside an array or struct. pass
3752 this along, do a foreach on the tracks which will do a foreach on the
3755 params.x = event->x;
3756 params.y = event->y;
3757 params.closest_track_name = NULL;
3758 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3759 params.closest_tp = NULL;
3761 if ( event->button != 1 )
3764 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3767 if ( !vtl->vl.visible || !vtl->tracks_visible )
3770 if ( vtl->current_tpl )
3772 /* 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.) */
3773 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
3774 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
3776 g_assert ( current_tr );
3778 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
3780 if ( current_tr->visible &&
3781 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
3782 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
3783 marker_begin_move ( t, event->x, event->y );
3787 vtl->last_tpl = vtl->current_tpl;
3788 vtl->last_tp_track_name = vtl->current_tp_track_name;
3791 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3793 if ( params.closest_tp )
3795 vtl->current_tpl = params.closest_tpl;
3796 vtl->current_tp_track_name = params.closest_track_name;
3797 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
3798 trw_layer_tpwin_init ( vtl );
3799 vik_layer_emit_update ( VIK_LAYER(vtl) );
3803 /* these aren't the droids you're looking for */
3807 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
3809 tool_ed_t *t = data;
3810 VikViewport *vvp = t->vvp;
3812 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3818 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3821 if ( event->state & GDK_CONTROL_MASK )
3823 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3824 if ( tp && tp != vtl->current_tpl->data )
3825 new_coord = tp->coord;
3827 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3830 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3831 marker_moveto ( t, x, y );
3839 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3841 tool_ed_t *t = data;
3842 VikViewport *vvp = t->vvp;
3844 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3846 if ( event->button != 1)
3851 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3854 if ( event->state & GDK_CONTROL_MASK )
3856 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3857 if ( tp && tp != vtl->current_tpl->data )
3858 new_coord = tp->coord;
3861 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
3863 marker_end_move ( t );
3865 /* diff dist is diff from orig */
3866 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3867 /* can't join with itself! */
3868 trw_layer_cancel_last_tp ( vtl );
3870 vik_layer_emit_update ( VIK_LAYER(vtl) );
3877 /*** Magic Scissors ***/
3878 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
3883 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3886 if ( !vtl ) return FALSE;
3887 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
3888 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
3890 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
3892 vtl->magic_scissors_coord = *new_end;
3894 vik_layer_emit_update ( VIK_LAYER(vtl) );
3895 /* remove last ' to:...' */
3896 if ( vtl->magic_scissors_current_track->comment ) {
3897 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
3898 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
3899 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
3900 last_to - vtl->magic_scissors_current_track->comment - 1);
3901 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
3906 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
3907 struct LatLon start, end;
3908 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
3909 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
3912 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
3913 vik_coord_to_latlon ( &(tmp), &end );
3914 vtl->magic_scissors_coord = tmp; /* for continuations */
3916 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
3917 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
3918 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
3920 vtl->magic_scissors_check_added_track = TRUE;
3921 vtl->magic_scissors_started = FALSE;
3924 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
3925 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
3926 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
3927 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
3928 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
3929 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
3932 /* see if anything was done -- a track was added or appended to */
3933 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
3936 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
3939 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
3941 vtl->magic_scissors_current_track = tr;
3943 g_free ( vtl->magic_scissors_added_track_name );
3944 vtl->magic_scissors_added_track_name = NULL;
3945 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
3946 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
3947 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
3948 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
3950 vtl->magic_scissors_check_added_track = FALSE;
3951 vtl->magic_scissors_append = FALSE;
3953 vik_layer_emit_update ( VIK_LAYER(vtl) );
3955 vtl->magic_scissors_started = TRUE;
3956 vtl->magic_scissors_coord = tmp;
3957 vtl->magic_scissors_current_track = NULL;
3962 /*** Show picture ****/
3964 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
3969 /* Params are: vvp, event, last match found or NULL */
3970 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
3972 if ( wp->image && wp->visible )
3974 gint x, y, slackx, slacky;
3975 GdkEventButton *event = (GdkEventButton *) params[1];
3977 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
3978 slackx = wp->image_width / 2;
3979 slacky = wp->image_height / 2;
3980 if ( x <= event->x + slackx && x >= event->x - slackx
3981 && y <= event->y + slacky && y >= event->y - slacky )
3983 params[2] = wp->image; /* we've found a match. however continue searching
3984 * since we want to find the last match -- that
3985 * is, the match that was drawn last. */
3990 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3992 gpointer params[3] = { vvp, event, NULL };
3993 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3995 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
3998 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
4000 ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
4003 gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
4004 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
4005 g_free ( quoted_file );
4006 if ( ! g_spawn_command_line_async ( cmd, &err ) )
4008 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch eog to open file.") );
4009 g_error_free ( err );
4012 #endif /* WINDOWS */
4013 return TRUE; /* found a match */
4016 return FALSE; /* go through other layers, searching for a match */
4019 /***************************************************************************
4021 ***************************************************************************/
4027 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
4029 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
4030 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
4033 static void create_thumbnails_thread ( GSList *pics, gpointer threaddata )
4035 guint total = g_slist_length(pics), done = 0;
4038 a_thumbnails_create ( (gchar *) pics->data );
4039 a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
4044 static void free_pics_slist ( GSList *pics )
4048 g_free ( pics->data );
4049 pics = g_slist_delete_link ( pics, pics );
4053 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
4055 if ( ! vtl->has_verified_thumbnails )
4057 GSList *pics = NULL;
4058 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
4061 gint len = g_slist_length ( pics );
4062 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
4063 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 );
4069 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
4071 return vtl->coord_mode;
4076 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
4078 vik_coord_convert ( &(wp->coord), *dest_mode );
4081 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
4083 vik_track_convert ( tr, *dest_mode );
4086 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
4088 if ( vtl->coord_mode != dest_mode )
4090 vtl->coord_mode = dest_mode;
4091 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
4092 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
4096 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
4098 return g_hash_table_lookup ( vtl->waypoints, name );
4101 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
4103 return g_hash_table_lookup ( vtl->tracks, name );
4106 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
4108 vtl->menu_selection = selection;
4111 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
4113 return(vtl->menu_selection);
4116 /* ----------- Downloading maps along tracks --------------- */
4118 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
4120 /* TODO: calculating based on current size of viewport */
4121 const gdouble w_at_zoom_0_125 = 0.0013;
4122 const gdouble h_at_zoom_0_125 = 0.0011;
4123 gdouble zoom_factor = zoom_level/0.125;
4125 wh->lat = h_at_zoom_0_125 * zoom_factor;
4126 wh->lon = w_at_zoom_0_125 * zoom_factor;
4128 return 0; /* all OK */
4131 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
4133 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
4134 (dist->lat >= ABS(to->north_south - from->north_south)))
4137 VikCoord *coord = g_malloc(sizeof(VikCoord));
4138 coord->mode = VIK_COORD_LATLON;
4140 if (ABS(gradient) < 1) {
4141 if (from->east_west > to->east_west)
4142 coord->east_west = from->east_west - dist->lon;
4144 coord->east_west = from->east_west + dist->lon;
4145 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
4147 if (from->north_south > to->north_south)
4148 coord->north_south = from->north_south - dist->lat;
4150 coord->north_south = from->north_south + dist->lat;
4151 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
4157 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
4159 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
4160 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
4162 VikCoord *next = from;
4164 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
4166 list = g_list_prepend(list, next);
4172 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
4174 typedef struct _Rect {
4179 #define GLRECT(iter) ((Rect *)((iter)->data))
4182 GList *rects_to_download = NULL;
4185 if (get_download_area_width(vvp, zoom_level, &wh))
4188 GList *iter = tr->trackpoints;
4192 gboolean new_map = TRUE;
4193 VikCoord *cur_coord, tl, br;
4196 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
4198 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4199 rect = g_malloc(sizeof(Rect));
4202 rect->center = *cur_coord;
4203 rects_to_download = g_list_prepend(rects_to_download, rect);
4208 gboolean found = FALSE;
4209 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4210 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
4221 GList *fillins = NULL;
4222 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
4223 /* seems that ATM the function get_next_coord works only for LATLON */
4224 if ( cur_coord->mode == VIK_COORD_LATLON ) {
4225 /* fill-ins for far apart points */
4226 GList *cur_rect, *next_rect;
4227 for (cur_rect = rects_to_download;
4228 (next_rect = cur_rect->next) != NULL;
4229 cur_rect = cur_rect->next) {
4230 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
4231 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
4232 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
4236 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
4239 GList *iter = fillins;
4241 cur_coord = (VikCoord *)(iter->data);
4242 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4243 rect = g_malloc(sizeof(Rect));
4246 rect->center = *cur_coord;
4247 rects_to_download = g_list_prepend(rects_to_download, rect);
4252 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4253 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
4257 for (iter = fillins; iter; iter = iter->next)
4259 g_list_free(fillins);
4261 if (rects_to_download) {
4262 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
4263 g_free(rect_iter->data);
4264 g_list_free(rects_to_download);
4268 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6])
4271 gint selected_map, default_map;
4272 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
4273 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
4274 gint selected_zoom, default_zoom;
4278 VikTrwLayer *vtl = pass_along[0];
4279 VikLayersPanel *vlp = pass_along[1];
4280 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4281 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
4283 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS);
4284 int num_maps = g_list_length(vmls);
4287 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
4291 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
4292 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
4294 gchar **np = map_names;
4295 VikMapsLayer **lp = map_layers;
4296 for (i = 0; i < num_maps; i++) {
4297 gboolean dup = FALSE;
4298 vml = (VikMapsLayer *)(vmls->data);
4299 for (j = 0; j < i; j++) { /* no duplicate allowed */
4300 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
4307 *np++ = vik_maps_layer_get_map_label(vml);
4313 num_maps = lp - map_layers;
4315 for (default_map = 0; default_map < num_maps; default_map++) {
4316 /* TODO: check for parent layer's visibility */
4317 if (VIK_LAYER(map_layers[default_map])->visible)
4320 default_map = (default_map == num_maps) ? 0 : default_map;
4322 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
4323 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
4324 if (cur_zoom == zoom_vals[default_zoom])
4327 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
4329 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
4332 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
4335 for (i = 0; i < num_maps; i++)
4336 g_free(map_names[i]);
4344 /**** lowest waypoint number calculation ***/
4345 static gint highest_wp_number_name_to_number(const gchar *name) {
4346 if ( strlen(name) == 3 ) {
4348 if ( n < 100 && name[0] != '0' )
4350 if ( n < 10 && name[0] != '0' )
4358 static void highest_wp_number_reset(VikTrwLayer *vtl)
4360 vtl->highest_wp_number = -1;
4363 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
4365 /* if is bigger that top, add it */
4366 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
4367 if ( new_wp_num > vtl->highest_wp_number )
4368 vtl->highest_wp_number = new_wp_num;
4371 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
4373 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
4374 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
4375 if ( vtl->highest_wp_number == old_wp_num ) {
4377 vtl->highest_wp_number --;
4379 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4380 /* search down until we find something that *does* exist */
4382 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
4383 vtl->highest_wp_number --;
4384 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4389 /* get lowest unused number */
4390 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
4393 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
4395 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
4396 return g_strdup(buf);