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 const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
228 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
230 static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord );
231 static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] );
232 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
233 static void trw_layer_goto_track_max_speed ( gpointer pass_along[5] );
234 static void trw_layer_goto_track_max_alt ( gpointer pass_along[5] );
235 static void trw_layer_goto_track_min_alt ( gpointer pass_along[5] );
236 static void trw_layer_auto_track_view ( gpointer pass_along[5] );
237 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
238 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
239 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6]);
240 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
241 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
242 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
243 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
244 static void trw_layer_new_wp ( gpointer lav[2] );
245 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
246 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
247 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
248 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
249 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
252 static void trw_layer_properties_item ( gpointer pass_along[5] );
253 static void trw_layer_goto_waypoint ( gpointer pass_along[5] );
254 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] );
256 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] );
257 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] );
258 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
261 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
262 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
264 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
265 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
267 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
268 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
270 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
271 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
272 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
273 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
275 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
276 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
277 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
278 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
279 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
281 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
282 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
283 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
284 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
285 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
286 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
287 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
288 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
289 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
290 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
291 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
292 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
293 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
294 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
295 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
296 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
297 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
298 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
299 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
300 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
303 static void cached_pixbuf_free ( CachedPixbuf *cp );
304 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
305 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
307 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
308 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
310 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
312 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
313 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
314 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
316 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
317 static void highest_wp_number_reset(VikTrwLayer *vtl);
318 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
319 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
322 static VikToolInterface trw_layer_tools[] = {
323 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
324 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
326 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
327 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
328 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
330 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
331 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
333 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
334 (VikToolMouseFunc) tool_edit_waypoint_click,
335 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
336 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
338 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
339 (VikToolMouseFunc) tool_edit_trackpoint_click,
340 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
341 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
343 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
344 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
346 { N_("Magic Scissors"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
347 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
349 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
351 /****** PARAMETERS ******/
353 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
354 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
356 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
357 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
360 static VikLayerParamScale params_scales[] = {
361 /* min max step digits */
362 { 1, 10, 1, 0 }, /* line_thickness */
363 { 0.0, 99.0, 1, 2 }, /* velocity_min */
364 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
365 /* 5 * step == how much to turn */
366 { 16, 128, 3.2, 0 }, /* image_size */
367 { 0, 255, 5, 0 }, /* image alpha */
368 { 5, 500, 5, 0 }, /* image cache_size */
369 { 0, 8, 1, 0 }, /* image cache_size */
370 { 1, 64, 1, 0 }, /* wpsize */
371 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
372 { 1, 100, 1, 0 }, /* stop_length */
375 VikLayerParam trw_layer_params[] = {
376 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
377 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
379 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
380 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
381 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
382 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
383 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
385 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
386 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
388 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
389 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
390 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
391 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
392 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
394 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
395 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
396 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
397 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
398 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
399 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
400 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
401 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
403 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
404 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
405 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
406 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
409 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 };
412 *** 1) Add to trw_layer_params and enumeration
413 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
416 /****** END PARAMETERS ******/
418 VikLayerInterface vik_trw_layer_interface = {
423 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
427 params_groups, /* params_groups */
428 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
432 (VikLayerFuncCreate) vik_trw_layer_create,
433 (VikLayerFuncRealize) vik_trw_layer_realize,
434 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
435 (VikLayerFuncFree) vik_trw_layer_free,
437 (VikLayerFuncProperties) NULL,
438 (VikLayerFuncDraw) vik_trw_layer_draw,
439 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
441 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
442 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
444 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
445 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
447 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
448 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
449 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
450 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
452 (VikLayerFuncMarshall) trw_layer_marshall,
453 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
455 (VikLayerFuncSetParam) trw_layer_set_param,
456 (VikLayerFuncGetParam) trw_layer_get_param,
458 (VikLayerFuncReadFileData) a_gpspoint_read_file,
459 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
461 (VikLayerFuncDeleteItem) trw_layer_del_item,
462 (VikLayerFuncCutItem) trw_layer_cut_item,
463 (VikLayerFuncCopyItem) trw_layer_copy_item,
464 (VikLayerFuncPasteItem) trw_layer_paste_item,
465 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
467 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
470 /* for copy & paste (I think?) */
478 GType vik_trw_layer_get_type ()
480 static GType vtl_type = 0;
484 static const GTypeInfo vtl_info =
486 sizeof (VikTrwLayerClass),
487 NULL, /* base_init */
488 NULL, /* base_finalize */
489 NULL, /* class init */
490 NULL, /* class_finalize */
491 NULL, /* class_data */
492 sizeof (VikTrwLayer),
494 NULL /* instance init */
496 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
502 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
504 static gpointer pass_along[5];
510 pass_along[1] = NULL;
511 pass_along[2] = GINT_TO_POINTER (subtype);
512 pass_along[3] = sublayer;
513 pass_along[4] = NULL;
515 trw_layer_delete_item ( pass_along );
518 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
520 static gpointer pass_along[5];
526 pass_along[1] = NULL;
527 pass_along[2] = GINT_TO_POINTER (subtype);
528 pass_along[3] = sublayer;
529 pass_along[4] = NULL;
531 trw_layer_copy_item_cb(pass_along);
532 trw_layer_cut_item_cb(pass_along);
535 static void trw_layer_copy_item_cb( gpointer pass_along[5])
537 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
538 gint subtype = GPOINTER_TO_INT (pass_along[2]);
539 gpointer * sublayer = pass_along[3];
543 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
546 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
551 static void trw_layer_cut_item_cb( gpointer pass_along[5])
553 trw_layer_copy_item_cb(pass_along);
554 trw_layer_delete_item(pass_along);
557 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
568 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
570 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
572 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
575 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
576 fi = g_malloc ( *len );
577 fi->len = strlen(sublayer) + 1;
578 memcpy(fi->data, sublayer, fi->len);
579 memcpy(fi->data + fi->len, id, il);
581 *item = (guint8 *)fi;
584 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
586 FlatItem *fi = (FlatItem *) item;
588 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
593 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
594 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
595 vik_trw_layer_add_waypoint ( vtl, name, w );
596 waypoint_convert(name, w, &vtl->coord_mode);
597 // Consider if redraw necessary for the new item
598 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
599 vik_layer_emit_update ( VIK_LAYER(vtl) );
602 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
606 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
607 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
608 vik_trw_layer_add_track ( vtl, name, t );
609 track_convert(name, t, &vtl->coord_mode);
610 // Consider if redraw necessary for the new item
611 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
612 vik_layer_emit_update ( VIK_LAYER(vtl) );
618 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
625 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
629 case PARAM_TV: vtl->tracks_visible = data.b; break;
630 case PARAM_WV: vtl->waypoints_visible = data.b; break;
631 case PARAM_DM: vtl->drawmode = data.u; break;
632 case PARAM_DP: vtl->drawpoints = data.b; break;
633 case PARAM_DE: vtl->drawelevation = data.b; break;
634 case PARAM_DS: vtl->drawstops = data.b; break;
635 case PARAM_DL: vtl->drawlines = data.b; break;
636 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
637 vtl->stop_length = data.u;
639 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
640 vtl->elevation_factor = data.u;
642 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
644 vtl->line_thickness = data.u;
645 trw_layer_new_track_gcs ( vtl, vp );
648 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
650 vtl->bg_line_thickness = data.u;
651 trw_layer_new_track_gcs ( vtl, vp );
656 /* Convert to store internally
657 NB file operation always in internal units (metres per second) */
658 vik_units_speed_t speed_units = a_vik_get_units_speed ();
659 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
660 vtl->velocity_min = data.d;
661 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
662 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
663 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
664 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
667 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
672 /* Convert to store internally
673 NB file operation always in internal units (metres per second) */
674 vik_units_speed_t speed_units = a_vik_get_units_speed ();
675 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
676 vtl->velocity_max = data.d;
677 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
678 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
679 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
680 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
683 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
686 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
687 case PARAM_DLA: vtl->drawlabels = data.b; break;
688 case PARAM_DI: vtl->drawimages = data.b; break;
689 case PARAM_IS: if ( data.u != vtl->image_size )
691 vtl->image_size = data.u;
692 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
693 g_queue_free ( vtl->image_cache );
694 vtl->image_cache = g_queue_new ();
697 case PARAM_IA: vtl->image_alpha = data.u; break;
698 case PARAM_ICS: vtl->image_cache_size = data.u;
699 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
700 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
702 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
703 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
704 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
705 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
706 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
707 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
708 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
713 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
715 VikLayerParamData rv;
718 case PARAM_TV: rv.b = vtl->tracks_visible; break;
719 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
720 case PARAM_DM: rv.u = vtl->drawmode; break;
721 case PARAM_DP: rv.b = vtl->drawpoints; break;
722 case PARAM_DE: rv.b = vtl->drawelevation; break;
723 case PARAM_EF: rv.u = vtl->elevation_factor; break;
724 case PARAM_DS: rv.b = vtl->drawstops; break;
725 case PARAM_SL: rv.u = vtl->stop_length; break;
726 case PARAM_DL: rv.b = vtl->drawlines; break;
727 case PARAM_LT: rv.u = vtl->line_thickness; break;
728 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
731 /* Convert to store internally
732 NB file operation always in internal units (metres per second) */
733 vik_units_speed_t speed_units = a_vik_get_units_speed ();
734 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
735 rv.d = vtl->velocity_min;
736 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
737 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
738 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
739 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
742 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
747 /* Convert to store internally
748 NB file operation always in internal units (metres per second) */
749 vik_units_speed_t speed_units = a_vik_get_units_speed ();
750 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
751 rv.d = vtl->velocity_max;
752 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
753 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
754 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
755 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
758 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
761 case PARAM_DLA: rv.b = vtl->drawlabels; break;
762 case PARAM_DI: rv.b = vtl->drawimages; break;
763 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
764 case PARAM_IS: rv.u = vtl->image_size; break;
765 case PARAM_IA: rv.u = vtl->image_alpha; break;
766 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
767 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
768 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
769 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
770 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
771 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
772 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
773 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
778 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
789 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
790 a_gpx_write_file(vtl, f);
791 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
794 g_file_get_contents(tmpname, &dd, &dl, NULL);
795 *len = sizeof(pl) + pl + dl;
796 *data = g_malloc(*len);
797 memcpy(*data, &pl, sizeof(pl));
798 memcpy(*data + sizeof(pl), pd, pl);
799 memcpy(*data + sizeof(pl) + pl, dd, dl);
808 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
810 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
816 memcpy(&pl, data, sizeof(pl));
818 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
821 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
822 g_critical("couldn't open temp file");
825 fwrite(data, len - pl - sizeof(pl), 1, f);
827 a_gpx_read_file(rv, f);
835 static GList * str_array_to_glist(gchar* data[])
839 for (p = (gpointer)data; *p; p++)
840 gl = g_list_prepend(gl, *p);
841 return(g_list_reverse(gl));
844 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
846 return (strcasecmp(s1, s2) == 0);
849 static guint strcase_hash(gconstpointer v)
851 /* 31 bit hash function */
854 gchar s[128]; /* malloc is too slow for reading big files */
857 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
858 p[i] = toupper(t[i]);
864 for (p += 1; *p != '\0'; p++)
865 h = (h << 5) - h + *p;
871 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
873 if (trw_layer_params[PARAM_DM].widget_data == NULL)
874 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
875 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
876 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
878 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
879 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
881 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
882 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
883 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
884 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
886 /* TODO: constants at top */
887 rv->waypoints_visible = rv->tracks_visible = TRUE;
888 rv->drawmode = drawmode;
889 rv->drawpoints = TRUE;
890 rv->drawstops = FALSE;
891 rv->drawelevation = FALSE;
892 rv->elevation_factor = 30;
893 rv->stop_length = 60;
894 rv->drawlines = TRUE;
895 rv->wplabellayout = NULL;
896 rv->wp_right_click_menu = NULL;
897 rv->waypoint_gc = NULL;
898 rv->waypoint_text_gc = NULL;
899 rv->waypoint_bg_gc = NULL;
901 rv->velocity_max = 5.0;
902 rv->velocity_min = 0.0;
903 rv->line_thickness = 1;
904 rv->bg_line_thickness = 0;
905 rv->current_wp = NULL;
906 rv->current_wp_name = NULL;
907 rv->current_track = NULL;
908 rv->current_tpl = NULL;
909 rv->current_tp_track_name = NULL;
910 rv->moving_tp = FALSE;
911 rv->moving_wp = FALSE;
913 rv->ct_sync_done = TRUE;
915 rv->magic_scissors_started = FALSE;
916 rv->magic_scissors_check_added_track = FALSE;
917 rv->magic_scissors_added_track_name = NULL;
918 rv->magic_scissors_current_track = NULL;
919 rv->magic_scissors_append = FALSE;
921 rv->waypoint_rightclick = FALSE;
923 rv->last_tp_track_name = NULL;
925 rv->image_cache = g_queue_new();
927 rv->image_alpha = 255;
928 rv->image_cache_size = 300;
929 rv->drawimages = TRUE;
930 rv->drawlabels = TRUE;
935 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
937 g_hash_table_destroy(trwlayer->waypoints);
938 g_hash_table_destroy(trwlayer->tracks);
940 /* ODC: replace with GArray */
941 trw_layer_free_track_gcs ( trwlayer );
943 if ( trwlayer->wp_right_click_menu )
944 gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) );
946 if ( trwlayer->wplabellayout != NULL)
947 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
949 if ( trwlayer->waypoint_gc != NULL )
950 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
952 if ( trwlayer->waypoint_text_gc != NULL )
953 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
955 if ( trwlayer->waypoint_bg_gc != NULL )
956 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
958 if ( trwlayer->waypoint_font != NULL )
959 gdk_font_unref ( trwlayer->waypoint_font );
961 if ( trwlayer->tpwin != NULL )
962 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
964 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
965 g_queue_free ( trwlayer->image_cache );
968 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
971 dp->xmpp = vik_viewport_get_xmpp ( vp );
972 dp->ympp = vik_viewport_get_ympp ( vp );
973 dp->width = vik_viewport_get_width ( vp );
974 dp->height = vik_viewport_get_height ( vp );
975 dp->center = vik_viewport_get_center ( vp );
976 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
977 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
982 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
983 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
984 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
986 dp->ce1 = dp->center->east_west-w2;
987 dp->ce2 = dp->center->east_west+w2;
988 dp->cn1 = dp->center->north_south-h2;
989 dp->cn2 = dp->center->north_south+h2;
990 } else if ( dp->lat_lon ) {
991 VikCoord upperleft, bottomright;
992 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
993 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
994 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
995 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
996 dp->ce1 = upperleft.east_west;
997 dp->ce2 = bottomright.east_west;
998 dp->cn1 = bottomright.north_south;
999 dp->cn2 = upperleft.north_south;
1002 dp->track_gc_iter = 0;
1005 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1007 static gdouble rv = 0;
1008 if ( tp1->has_timestamp && tp2->has_timestamp )
1010 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1011 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1014 return VIK_TRW_LAYER_TRACK_GC_MIN;
1015 else if ( vtl->velocity_min >= vtl->velocity_max )
1016 return VIK_TRW_LAYER_TRACK_GC_MAX;
1018 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1020 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1021 return VIK_TRW_LAYER_TRACK_GC_MAX;
1025 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1028 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1030 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1031 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1032 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1033 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1036 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1038 /* TODO: this function is a mess, get rid of any redundancy */
1039 GList *list = track->trackpoints;
1041 gboolean useoldvals = TRUE;
1043 gboolean drawpoints;
1045 gboolean drawelevation;
1046 gdouble min_alt, max_alt, alt_diff = 0;
1048 const guint8 tp_size_reg = 2;
1049 const guint8 tp_size_cur = 4;
1052 if ( dp->vtl->drawelevation )
1054 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1055 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1056 alt_diff = max_alt - min_alt;
1059 if ( ! track->visible )
1062 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1063 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1064 trw_layer_draw_track ( name, track, dp, TRUE );
1066 if ( drawing_white_background )
1067 drawpoints = drawstops = FALSE;
1069 drawpoints = dp->vtl->drawpoints;
1070 drawstops = dp->vtl->drawstops;
1073 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1074 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1076 if ( track == dp->vtl->current_track )
1077 main_gc = dp->vtl->current_track_gc;
1079 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1082 int x, y, oldx, oldy;
1083 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1085 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1087 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1089 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1091 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1092 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1098 while ((list = g_list_next(list)))
1100 tp = VIK_TRACKPOINT(list->data);
1101 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1103 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1104 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1105 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1106 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1107 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1109 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1111 if ( drawpoints && ! drawing_white_background )
1114 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1117 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1118 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 );
1121 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 );
1124 if ((!tp->newsegment) && (dp->vtl->drawlines))
1126 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1128 /* UTM only: zone check */
1129 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1130 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1132 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1133 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1134 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1138 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1140 if ( drawing_white_background ) {
1141 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1145 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1146 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1148 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1149 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1153 tmp[1].y = oldy-FIXALTITUDE(list->data);
1155 tmp[2].y = y-FIXALTITUDE(list->next->data);
1160 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1161 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1163 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1164 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1166 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1176 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1178 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1179 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1181 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1182 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1183 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1184 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1187 if ( drawing_white_background )
1188 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1190 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1194 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1195 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1202 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1203 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1204 dp->track_gc_iter = 0;
1207 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1208 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1210 trw_layer_draw_track ( name, track, dp, FALSE );
1213 static void cached_pixbuf_free ( CachedPixbuf *cp )
1215 g_object_unref ( G_OBJECT(cp->pixbuf) );
1216 g_free ( cp->image );
1219 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1221 return strcmp ( cp->image, name );
1224 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1227 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1228 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1229 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1232 GdkPixbuf *sym = NULL;
1233 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1235 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1237 if ( wp->image && dp->vtl->drawimages )
1239 GdkPixbuf *pixbuf = NULL;
1242 if ( dp->vtl->image_alpha == 0)
1245 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1247 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1250 gchar *image = wp->image;
1251 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1252 if ( ! regularthumb )
1254 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1255 image = "\x12\x00"; /* this shouldn't occur naturally. */
1259 CachedPixbuf *cp = NULL;
1260 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1261 if ( dp->vtl->image_size == 128 )
1262 cp->pixbuf = regularthumb;
1265 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1266 g_assert ( cp->pixbuf );
1267 g_object_unref ( G_OBJECT(regularthumb) );
1269 cp->image = g_strdup ( image );
1271 /* needed so 'click picture' tool knows how big the pic is; we don't
1272 * store it in cp because they may have been freed already. */
1273 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1274 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1276 g_queue_push_head ( dp->vtl->image_cache, cp );
1277 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1278 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1280 pixbuf = cp->pixbuf;
1284 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1290 w = gdk_pixbuf_get_width ( pixbuf );
1291 h = gdk_pixbuf_get_height ( pixbuf );
1293 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1295 if ( dp->vtl->image_alpha == 255 )
1296 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1298 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1300 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1304 /* DRAW ACTUAL DOT */
1305 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1306 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 );
1308 else if ( wp == dp->vtl->current_wp ) {
1309 switch ( dp->vtl->wp_symbol ) {
1310 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;
1311 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;
1312 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;
1313 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 );
1314 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 );
1318 switch ( dp->vtl->wp_symbol ) {
1319 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;
1320 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;
1321 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;
1322 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 );
1323 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;
1327 if ( dp->vtl->drawlabels )
1329 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1330 gint label_x, label_y;
1332 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1333 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1334 label_x = x - width/2;
1336 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1338 label_y = y - dp->vtl->wp_size - height - 2;
1340 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1341 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1346 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1348 static struct DrawingParams dp;
1349 g_assert ( l != NULL );
1351 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1354 if ( l->tracks_visible )
1355 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1357 if (l->waypoints_visible)
1358 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1361 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1364 if ( vtl->track_bg_gc )
1366 g_object_unref ( vtl->track_bg_gc );
1367 vtl->track_bg_gc = NULL;
1369 if ( vtl->current_track_gc )
1371 g_object_unref ( vtl->current_track_gc );
1372 vtl->current_track_gc = NULL;
1375 if ( ! vtl->track_gc )
1377 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1378 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1379 g_array_free ( vtl->track_gc, TRUE );
1380 vtl->track_gc = NULL;
1383 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1385 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1386 gint width = vtl->line_thickness;
1388 if ( vtl->track_gc )
1389 trw_layer_free_track_gcs ( vtl );
1391 if ( vtl->track_bg_gc )
1392 g_object_unref ( vtl->track_bg_gc );
1393 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1395 if ( vtl->current_track_gc )
1396 g_object_unref ( vtl->current_track_gc );
1397 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1398 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1400 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1402 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1404 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1405 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1406 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1407 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1408 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1409 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1410 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1411 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1412 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1413 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1415 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1417 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1419 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1422 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1424 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1425 PangoFontDescription *pfd;
1426 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1427 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1428 pango_layout_set_font_description (rv->wplabellayout, pfd);
1429 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1430 pango_font_description_free (pfd);
1432 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1434 trw_layer_new_track_gcs ( rv, vp );
1436 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1437 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1438 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1439 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1441 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1443 rv->has_verified_thumbnails = FALSE;
1444 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1446 rv->wp_draw_symbols = TRUE;
1448 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1450 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1455 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] )
1457 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1459 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1460 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 );
1462 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1465 *new_iter = *((GtkTreeIter *) pass_along[1]);
1466 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1468 if ( ! track->visible )
1469 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1472 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] )
1474 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1475 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1476 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 );
1478 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
1481 *new_iter = *((GtkTreeIter *) pass_along[1]);
1482 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1484 if ( ! wp->visible )
1485 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1489 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1492 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1494 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1495 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1497 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1499 if ( ! vtl->tracks_visible )
1500 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1502 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1504 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1505 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1507 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1510 if ( ! vtl->waypoints_visible )
1511 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1513 pass_along[0] = &(vtl->waypoints_iter);
1514 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1516 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1520 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1524 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1525 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1526 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1528 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1530 return (t->visible ^= 1);
1534 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1536 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1538 return (t->visible ^= 1);
1546 // Structure to hold multiple track information for a layer
1555 * Build up layer multiple track information via updating the tooltip_tracks structure
1557 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1559 tt->length = tt->length + vik_track_get_length (tr);
1561 // Ensure times are available
1562 if ( tr->trackpoints &&
1563 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1564 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1567 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1568 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1570 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1571 // Hence initialize to the first 'proper' value
1572 if ( tt->start_time == 0 )
1573 tt->start_time = t1;
1574 if ( tt->end_time == 0 )
1577 // Update find the earliest / last times
1578 if ( t1 < tt->start_time )
1579 tt->start_time = t1;
1580 if ( t2 > tt->end_time )
1583 // Keep track of total time
1584 // there maybe gaps within a track (eg segments)
1585 // but this should be generally good enough for a simple indicator
1586 tt->duration = tt->duration + (int)(t2-t1);
1591 * Generate tooltip text for the layer.
1592 * This is relatively complicated as it considers information for
1593 * no tracks, a single track or multiple tracks
1594 * (which may or may not have timing information)
1596 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1607 static gchar tmp_buf[128];
1610 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1612 // Safety check - I think these should always be valid
1613 if ( vtl->tracks && vtl->waypoints ) {
1614 tooltip_tracks tt = { 0.0, 0, 0 };
1615 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1617 GDate* gdate_start = g_date_new ();
1618 g_date_set_time_t (gdate_start, tt.start_time);
1620 GDate* gdate_end = g_date_new ();
1621 g_date_set_time_t (gdate_end, tt.end_time);
1623 if ( g_date_compare (gdate_start, gdate_end) ) {
1624 // Dates differ so print range on separate line
1625 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1626 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1627 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1630 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1631 if ( tt.start_time != 0 )
1632 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1636 if ( tt.length > 0.0 ) {
1637 gdouble len_in_units;
1639 // Setup info dependent on distance units
1640 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1641 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1642 len_in_units = VIK_METERS_TO_MILES(tt.length);
1645 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1646 len_in_units = tt.length/1000.0;
1649 // Timing information if available
1651 if ( tt.duration > 0 ) {
1652 g_snprintf (tbuf1, sizeof(tbuf1),
1653 _(" in %d:%02d hrs:mins"),
1654 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1656 g_snprintf (tbuf2, sizeof(tbuf2),
1657 _("\n%sTotal Length %.1f %s%s"),
1658 tbuf3, len_in_units, tbuf4, tbuf1);
1661 // Put together all the elements to form compact tooltip text
1662 g_snprintf (tmp_buf, sizeof(tmp_buf),
1663 _("Tracks: %d - Waypoints: %d%s"),
1664 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1666 g_date_free (gdate_start);
1667 g_date_free (gdate_end);
1674 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1678 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1679 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1680 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1682 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1684 // Could be a better way of handling strings - but this works...
1685 gchar time_buf1[20];
1686 gchar time_buf2[20];
1687 time_buf1[0] = '\0';
1688 time_buf2[0] = '\0';
1689 static gchar tmp_buf[100];
1690 // Compact info: Short date eg (11/20/99), duration and length
1691 // Hopefully these are the things that are most useful and so promoted into the tooltip
1692 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1693 // %x The preferred date representation for the current locale without the time.
1694 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1695 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1696 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1698 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1701 // Get length and consider the appropriate distance units
1702 gdouble tr_len = vik_track_get_length(tr);
1703 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1704 switch (dist_units) {
1705 case VIK_UNITS_DISTANCE_KILOMETRES:
1706 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1708 case VIK_UNITS_DISTANCE_MILES:
1709 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1718 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1720 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1721 // NB It's OK to return NULL
1731 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1736 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1738 return l->waypoints;
1741 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1743 static VikCoord fixme;
1744 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1745 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1746 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1747 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1748 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1749 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1750 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1751 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1752 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1755 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1758 static VikCoord fixme;
1762 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1763 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1764 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1765 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1766 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1767 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1768 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1769 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1770 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1775 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1777 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1778 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1780 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1781 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1782 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1783 maxmin[0].lat = wpt_maxmin[0].lat;
1786 maxmin[0].lat = trk_maxmin[0].lat;
1788 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1789 maxmin[0].lon = wpt_maxmin[0].lon;
1792 maxmin[0].lon = trk_maxmin[0].lon;
1794 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1795 maxmin[1].lat = wpt_maxmin[1].lat;
1798 maxmin[1].lat = trk_maxmin[1].lat;
1800 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1801 maxmin[1].lon = wpt_maxmin[1].lon;
1804 maxmin[1].lon = trk_maxmin[1].lon;
1808 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1810 /* 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... */
1811 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1812 trw_layer_find_maxmin (vtl, maxmin);
1813 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1817 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1818 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1823 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1826 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1827 goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord );
1829 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1832 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1834 /* First set the center [in case previously viewing from elsewhere] */
1835 /* Then loop through zoom levels until provided positions are in view */
1836 /* This method is not particularly fast - but should work well enough */
1837 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1839 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1840 vik_viewport_set_center_coord ( vvp, &coord );
1842 /* Convert into definite 'smallest' and 'largest' positions */
1843 struct LatLon minmin;
1844 if ( maxmin[0].lat < maxmin[1].lat )
1845 minmin.lat = maxmin[0].lat;
1847 minmin.lat = maxmin[1].lat;
1849 struct LatLon maxmax;
1850 if ( maxmin[0].lon > maxmin[1].lon )
1851 maxmax.lon = maxmin[0].lon;
1853 maxmax.lon = maxmin[1].lon;
1855 /* Never zoom in too far - generally not that useful, as too close ! */
1856 /* Always recalculate the 'best' zoom level */
1858 vik_viewport_set_zoom ( vvp, zoom );
1860 gdouble min_lat, max_lat, min_lon, max_lon;
1861 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1862 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1863 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1864 /* NB I think the logic used in this test to determine if the bounds is within view
1865 fails if track goes across 180 degrees longitude.
1866 Hopefully that situation is not too common...
1867 Mind you viking doesn't really do edge locations to well anyway */
1868 if ( min_lat < minmin.lat &&
1869 max_lat > minmin.lat &&
1870 min_lon < maxmax.lon &&
1871 max_lon > maxmax.lon )
1872 /* Found within zoom level */
1877 vik_viewport_set_zoom ( vvp, zoom );
1881 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
1883 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
1884 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1885 trw_layer_find_maxmin (vtl, maxmin);
1886 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1889 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
1894 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
1896 if ( vik_trw_layer_auto_set_view ( VIK_TRW_LAYER(layer_and_vlp[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(layer_and_vlp[1])) ) ) {
1897 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
1900 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1903 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
1905 GtkWidget *file_selector;
1907 gboolean failed = FALSE;
1908 file_selector = gtk_file_chooser_dialog_new (title,
1910 GTK_FILE_CHOOSER_ACTION_SAVE,
1911 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1912 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1914 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
1916 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
1918 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
1919 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
1921 gtk_widget_hide ( file_selector );
1922 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
1927 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
1929 gtk_widget_hide ( file_selector );
1930 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
1935 gtk_widget_destroy ( file_selector );
1937 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
1940 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
1942 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
1945 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
1947 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
1950 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
1952 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX );
1955 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
1957 gpointer layer_and_vlp[2];
1958 layer_and_vlp[0] = pass_along[0];
1959 layer_and_vlp[1] = pass_along[1];
1961 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
1962 gchar *auto_save_name = g_strdup ( pass_along[3] );
1963 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
1964 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
1966 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
1968 g_free ( auto_save_name );
1971 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
1973 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
1974 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
1975 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
1976 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1978 GTK_RESPONSE_REJECT,
1980 GTK_RESPONSE_ACCEPT,
1983 GtkWidget *label, *entry;
1984 label = gtk_label_new(_("Waypoint Name:"));
1985 entry = gtk_entry_new();
1987 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
1988 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
1989 gtk_widget_show_all ( label );
1990 gtk_widget_show_all ( entry );
1992 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
1994 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
1997 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2000 for ( i = strlen(upname)-1; i >= 0; i-- )
2001 upname[i] = toupper(upname[i]);
2003 wp = g_hash_table_lookup ( wps, upname );
2006 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2009 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2010 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2011 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 ) );
2018 gtk_widget_destroy ( dia );
2021 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2023 gchar *name = highest_wp_number_get(vtl);
2024 VikWaypoint *wp = vik_waypoint_new();
2025 wp->coord = *def_coord;
2027 if ( a_dialog_new_waypoint ( w, &name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) )
2030 vik_trw_layer_add_waypoint ( vtl, name, wp );
2033 vik_waypoint_free(wp);
2037 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2040 struct LatLon one_ll, two_ll;
2041 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2043 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2044 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2045 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2046 VikViewport *vvp = vik_window_viewport(vw);
2047 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2048 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2049 vik_coord_to_latlon(&one, &one_ll);
2050 vik_coord_to_latlon(&two, &two_ll);
2051 if (one_ll.lat > two_ll.lat) {
2052 maxmin[0].lat = one_ll.lat;
2053 maxmin[1].lat = two_ll.lat;
2056 maxmin[0].lat = two_ll.lat;
2057 maxmin[1].lat = one_ll.lat;
2059 if (one_ll.lon > two_ll.lon) {
2060 maxmin[0].lon = one_ll.lon;
2061 maxmin[1].lon = two_ll.lon;
2064 maxmin[0].lon = two_ll.lon;
2065 maxmin[1].lon = one_ll.lon;
2067 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2070 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2072 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2073 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2074 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2076 trw_layer_find_maxmin (vtl, maxmin);
2077 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2080 static void trw_layer_new_wp ( gpointer lav[2] )
2082 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2083 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2084 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2085 instead return true if you want to update. */
2086 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 )
2087 vik_layers_panel_emit_update ( vlp );
2090 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2092 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2093 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2095 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2096 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2097 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2098 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2099 vik_layers_panel_emit_update ( vlp );
2103 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2105 /* NB do not care if wp is visible or not */
2106 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2109 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2111 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2112 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2114 /* Only 1 waypoint - jump straight to it */
2115 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2116 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2117 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2119 /* If at least 2 waypoints - find center and then zoom to fit */
2120 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2122 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2123 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2124 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2127 vik_layers_panel_emit_update ( vlp );
2130 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2132 static gpointer pass_along[2];
2134 GtkWidget *export_submenu;
2135 GtkWidget *wikipedia_submenu;
2136 pass_along[0] = vtl;
2137 pass_along[1] = vlp;
2139 item = gtk_menu_item_new();
2140 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2141 gtk_widget_show ( item );
2143 item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") );
2144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2145 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2146 gtk_widget_show ( item );
2148 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2149 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2150 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2151 gtk_widget_show ( item );
2153 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2155 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2156 gtk_widget_show ( item );
2158 item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2159 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2160 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2161 gtk_widget_show ( item );
2163 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint") );
2164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2165 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2166 gtk_widget_show ( item );
2168 export_submenu = gtk_menu_new ();
2169 item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
2170 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2171 gtk_widget_show ( item );
2172 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2174 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point") );
2175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2176 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2177 gtk_widget_show ( item );
2179 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper") );
2180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2181 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2182 gtk_widget_show ( item );
2184 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX") );
2185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2186 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2187 gtk_widget_show ( item );
2189 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
2190 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2191 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2192 gtk_widget_show ( item );
2194 #ifdef VIK_CONFIG_GEONAMES
2195 wikipedia_submenu = gtk_menu_new();
2196 item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2197 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2198 gtk_widget_show(item);
2199 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2201 item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2203 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2204 gtk_widget_show ( item );
2206 item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
2207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2208 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2209 gtk_widget_show ( item );
2212 #ifdef VIK_CONFIG_OPENSTREETMAP
2213 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
2214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2215 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2216 gtk_widget_show ( item );
2219 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2220 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2222 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2223 gtk_widget_show ( item );
2226 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2227 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2229 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2230 gtk_widget_show ( item );
2234 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2236 if ( VIK_LAYER(vtl)->realized )
2238 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2240 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2243 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2244 // Visibility column always needed for waypoints
2245 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2246 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2248 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2250 // Actual setting of visibility dependent on the waypoint
2251 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2252 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2253 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2257 highest_wp_number_add_wp(vtl, name);
2258 g_hash_table_insert ( vtl->waypoints, name, wp );
2262 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2264 if ( VIK_LAYER(vtl)->realized )
2266 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2268 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2271 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2272 // Visibility column always needed for tracks
2273 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2274 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2276 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2278 // Actual setting of visibility dependent on the track
2279 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2280 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2281 g_hash_table_insert ( vtl->tracks_iters, name, iter );
2285 g_hash_table_insert ( vtl->tracks, name, t );
2289 /* to be called whenever a track has been deleted or may have been changed. */
2290 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2292 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2293 trw_layer_cancel_current_tp ( vtl, FALSE );
2294 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2295 trw_layer_cancel_last_tp ( vtl );
2298 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2301 gchar *newname = g_strdup(name);
2302 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2303 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2304 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2306 newname = new_newname;
2312 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2314 vik_trw_layer_add_waypoint ( vtl,
2315 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2318 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2320 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
2321 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2322 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2323 vik_track_free ( tr );
2324 vtl->magic_scissors_append = FALSE; /* this means we have added it */
2326 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2327 vik_trw_layer_add_track ( vtl, new_name, tr );
2329 if ( vtl->magic_scissors_check_added_track ) {
2330 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2331 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2332 g_free ( vtl->magic_scissors_added_track_name );
2333 vtl->magic_scissors_added_track_name = g_strdup(new_name);
2338 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2340 *l = g_list_append(*l, (gpointer)name);
2343 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2345 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2346 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2348 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2349 vik_trw_layer_delete_track(vtl_src, name);
2350 vik_trw_layer_add_track(vtl_dest, newname, t);
2352 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2354 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2355 vik_trw_layer_delete_waypoint(vtl_src, name);
2356 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2360 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2362 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2363 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2365 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2366 GList *items = NULL;
2369 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2370 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2372 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2373 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2378 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2379 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2381 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2388 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2389 trw_layer_move_item(vtl_src, vtl_dest, name, type);
2394 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2396 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2397 gboolean was_visible = FALSE;
2401 was_visible = t->visible;
2402 if ( t == vtl->current_track )
2403 vtl->current_track = NULL;
2404 if ( t == vtl->magic_scissors_current_track )
2405 vtl->magic_scissors_current_track = NULL;
2407 /* could be current_tp, so we have to check */
2408 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2410 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2411 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2412 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2414 /* do this last because trk_name may be pointing to actual orig key */
2415 g_hash_table_remove ( vtl->tracks, trk_name );
2420 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2422 gboolean was_visible = FALSE;
2425 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2429 if ( wp == vtl->current_wp ) {
2430 vtl->current_wp = NULL;
2431 vtl->current_wp_name = NULL;
2432 vtl->moving_wp = FALSE;
2435 was_visible = wp->visible;
2436 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2437 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2438 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2440 highest_wp_number_remove_wp(vtl, wp_name);
2441 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2447 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2449 vik_treeview_item_delete (vt, it );
2452 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2455 vtl->current_track = NULL;
2456 vtl->magic_scissors_current_track = NULL;
2457 if (vtl->current_tp_track_name)
2458 trw_layer_cancel_current_tp(vtl, FALSE);
2459 if (vtl->last_tp_track_name)
2460 trw_layer_cancel_last_tp ( vtl );
2462 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2463 g_hash_table_remove_all(vtl->tracks_iters);
2464 g_hash_table_remove_all(vtl->tracks);
2466 /* TODO: only update if the layer is visible (ticked) */
2467 vik_layer_emit_update ( VIK_LAYER(vtl) );
2470 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2472 vtl->current_wp = NULL;
2473 vtl->current_wp_name = NULL;
2474 vtl->moving_wp = FALSE;
2476 highest_wp_number_reset(vtl);
2478 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2479 g_hash_table_remove_all(vtl->waypoints_iters);
2480 g_hash_table_remove_all(vtl->waypoints);
2482 /* TODO: only update if the layer is visible (ticked) */
2483 vik_layer_emit_update ( VIK_LAYER(vtl) );
2486 static void trw_layer_delete_item ( gpointer pass_along[5] )
2488 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2489 gboolean was_visible = FALSE;
2490 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2492 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2496 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2499 vik_layer_emit_update ( VIK_LAYER(vtl) );
2503 static void trw_layer_properties_item ( gpointer pass_along[5] )
2505 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2506 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2508 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2511 if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) )
2513 if ( VIK_LAYER(vtl)->visible )
2514 vik_layer_emit_update ( VIK_LAYER(vtl) );
2519 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2522 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2524 pass_along[1], /* vlp */
2525 pass_along[3] /* track name */);
2530 static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord )
2532 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord );
2533 vik_layers_panel_emit_update ( vlp );
2536 static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] )
2538 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2539 if ( trps && trps->data )
2540 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2543 static void trw_layer_goto_track_center ( gpointer pass_along[5] )
2545 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2546 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2547 if ( trps && *trps )
2549 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2551 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2552 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2553 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2554 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2555 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord);
2559 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2561 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2562 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2564 vtl->current_track = track;
2565 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2567 if ( track->trackpoints )
2568 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2572 * extend a track using magic scissors
2574 static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2576 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2577 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2578 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2580 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2581 vtl->magic_scissors_coord = last_coord;
2582 vtl->magic_scissors_current_track = track;
2583 vtl->magic_scissors_started = TRUE;
2585 if ( track->trackpoints )
2586 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &last_coord) ;
2590 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2592 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2593 /* Also warn if overwrite old elevation data */
2594 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2596 vik_track_apply_dem_data ( track );
2599 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2601 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2604 trps = g_list_last(trps);
2605 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord));
2608 static void trw_layer_goto_track_max_speed ( gpointer pass_along[5] )
2610 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2613 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2616 static void trw_layer_goto_track_max_alt ( gpointer pass_along[5] )
2618 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2621 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2624 static void trw_layer_goto_track_min_alt ( gpointer pass_along[5] )
2626 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2629 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(vtp->coord));
2633 * Automatically change the viewport to center on the track and zoom to see the extent of the track
2635 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2637 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2638 if ( trps && *trps )
2640 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2641 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2643 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(pass_along[1])), maxmin );
2644 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2648 /*************************************
2649 * merge/split by time routines
2650 *************************************/
2652 /* called for each key in track hash table.
2653 * If the current track has time stamp, add it to the result,
2654 * except the one pointed by "exclude".
2655 * set exclude to NULL if there is no exclude to check.
2656 * Not that result is in reverse (for performance reason).
2662 static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2664 twt_udata *user_data = udata;
2665 VikTrackpoint *p1, *p2;
2667 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2671 if (VIK_TRACK(value)->trackpoints) {
2672 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2673 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2675 if (!p1->has_timestamp || !p2->has_timestamp) {
2676 g_print("no timestamp\n");
2682 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2685 /* called for each key in track hash table. if original track user_data[1] is close enough
2686 * to the passed one, add it to list in user_data[0]
2688 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2691 VikTrackpoint *p1, *p2;
2693 GList **nearby_tracks = ((gpointer *)user_data)[0];
2694 GList *orig_track = ((gpointer *)user_data)[1];
2695 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
2698 * detect reasons for not merging, and return
2699 * if no reason is found not to merge, then do it.
2702 if (VIK_TRACK(value)->trackpoints == orig_track) {
2706 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2707 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2709 if (VIK_TRACK(value)->trackpoints) {
2710 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2711 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2713 if (!p1->has_timestamp || !p2->has_timestamp) {
2714 g_print("no timestamp\n");
2718 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2719 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2721 abs(p1->timestamp - t2) < thr*60)
2728 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2731 /* comparison function used to sort tracks; a and b are hash table keys */
2732 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2734 GHashTable *tracks = user_data;
2737 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2738 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2740 if (t1 < t2) return -1;
2741 if (t1 > t2) return 1;
2745 /* comparison function used to sort trackpoints */
2746 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2748 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2750 if (t1 < t2) return -1;
2751 if (t1 > t2) return 1;
2755 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2757 * comparison function which can be used to sort tracks or waypoints by name
2759 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
2761 const gchar* namea = (const gchar*) a;
2762 const gchar* nameb = (const gchar*) b;
2763 if ( namea == NULL || nameb == NULL)
2766 // Same sort method as used in the vik_treeview_*_alphabetize functions
2767 return strcmp ( namea, nameb );
2771 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2773 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2774 gchar *orig_track_name = pass_along[3];
2775 GList *tracks_with_timestamp = NULL;
2776 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2778 if (track->trackpoints &&
2779 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2780 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2787 udata.result = &tracks_with_timestamp;
2788 udata.exclude = track->trackpoints;
2789 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2790 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2793 if (!tracks_with_timestamp) {
2794 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2798 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2799 // Sort alphabetically for user presentation
2800 tracks_with_timestamp = g_list_sort_with_data (tracks_with_timestamp, sort_alphabetically, NULL);
2803 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2804 tracks_with_timestamp, TRUE,
2805 _("Merge with..."), _("Select track to merge with"));
2806 g_list_free(tracks_with_timestamp);
2811 for (l = merge_list; l != NULL; l = g_list_next(l)) {
2812 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2814 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2815 merge_track->trackpoints = NULL;
2816 vik_trw_layer_delete_track(vtl, l->data);
2817 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2820 /* TODO: free data before free merge_list */
2821 for (l = merge_list; l != NULL; l = g_list_next(l))
2823 g_list_free(merge_list);
2824 vik_layer_emit_update( VIK_LAYER(vtl) );
2828 /* merge by time routine */
2829 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2831 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2832 gchar *orig_track_name = strdup(pass_along[3]);
2835 GList *nearby_tracks;
2838 static guint thr = 1;
2839 guint track_count = 0;
2841 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2842 _("Merge Threshold..."),
2843 _("Merge when time between tracks less than:"),
2845 free(orig_track_name);
2849 /* merge tracks until we can't */
2850 nearby_tracks = NULL;
2854 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2855 trps = track->trackpoints;
2860 if (nearby_tracks) {
2861 g_list_free(nearby_tracks);
2862 nearby_tracks = NULL;
2865 t1 = ((VikTrackpoint *)trps->data)->timestamp;
2866 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
2868 /* g_print("Original track times: %d and %d\n", t1, t2); */
2869 params[0] = &nearby_tracks;
2871 params[2] = GUINT_TO_POINTER (thr);
2873 /* get a list of adjacent-in-time tracks */
2874 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
2876 /* add original track */
2877 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
2881 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
2882 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
2883 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
2884 GList *l = nearby_tracks;
2885 VikTrack *tr = vik_track_new();
2886 tr->visible = track->visible;
2891 t1 = get_first_trackpoint(l)->timestamp;
2892 t2 = get_last_trackpoint(l)->timestamp;
2893 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
2897 /* remove trackpoints from merged track, delete track */
2898 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
2899 get_track(l)->trackpoints = NULL;
2900 vik_trw_layer_delete_track(vtl, l->data);
2905 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
2906 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
2908 #undef get_first_trackpoint
2909 #undef get_last_trackpoint
2912 } while (track_count > 1);
2913 g_list_free(nearby_tracks);
2914 free(orig_track_name);
2915 vik_layer_emit_update( VIK_LAYER(vtl) );
2918 /* split by time routine */
2919 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
2921 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2922 GList *trps = track->trackpoints;
2924 GList *newlists = NULL;
2925 GList *newtps = NULL;
2927 static guint thr = 1;
2934 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2935 _("Split Threshold..."),
2936 _("Split when time between trackpoints exceeds:"),
2941 /* iterate through trackpoints, and copy them into new lists without touching original list */
2942 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
2946 ts = VIK_TRACKPOINT(iter->data)->timestamp;
2948 g_print("panic: ts < prev_ts: this should never happen!\n");
2951 if (ts - prev_ts > thr*60) {
2952 /* flush accumulated trackpoints into new list */
2953 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2957 /* accumulate trackpoint copies in newtps, in reverse order */
2958 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
2960 iter = g_list_next(iter);
2963 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
2966 /* put lists of trackpoints into tracks */
2973 tr = vik_track_new();
2974 tr->visible = track->visible;
2975 tr->trackpoints = (GList *)(iter->data);
2977 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
2978 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
2979 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
2980 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
2982 iter = g_list_next(iter);
2984 g_list_free(newlists);
2985 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
2986 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
2989 /* end of split/merge routines */
2992 static void trw_layer_goto_waypoint ( gpointer pass_along[5] )
2994 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2996 goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) );
2999 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] )
3001 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3002 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3006 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3008 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3013 if (strcmp(newname, sublayer) == 0 )
3016 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3017 if (g_hash_table_lookup( l->waypoints, newname))
3019 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3024 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3025 g_hash_table_steal ( l->waypoints_iters, sublayer );
3027 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3028 highest_wp_number_remove_wp(l, sublayer);
3029 g_hash_table_remove ( l->waypoints, sublayer );
3031 rv = g_strdup(newname);
3033 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3035 highest_wp_number_add_wp(l, rv);
3036 g_hash_table_insert ( l->waypoints, rv, wp );
3037 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3039 /* it hasn't been updated yet so we pass new name */
3040 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3041 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3044 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3047 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3054 if (strcmp(newname, sublayer) == 0)
3057 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3058 if (g_hash_table_lookup( l->tracks, newname))
3060 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3065 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3066 g_hash_table_steal ( l->tracks, sublayer );
3068 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3069 g_hash_table_steal ( l->tracks_iters, sublayer );
3071 rv = g_strdup(newname);
3073 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3075 g_hash_table_insert ( l->tracks, rv, tr );
3076 g_hash_table_insert ( l->tracks_iters, rv, iter );
3078 /* don't forget about current_tp_track_name, update that too */
3079 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3081 l->current_tp_track_name = rv;
3083 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3085 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3086 l->last_tp_track_name = rv;
3088 g_free ( orig_key );
3090 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3091 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3094 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3100 static gboolean is_valid_geocache_name ( gchar *str )
3102 gint len = strlen ( str );
3103 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]));
3106 static void trw_layer_track_use_with_filter ( gpointer *pass_along )
3108 gchar *track_name = (gchar *) pass_along[3];
3109 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3110 a_acquire_set_filter_track ( tr, track_name );
3113 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3115 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3116 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3119 static void trw_layer_track_google_route_webpage( gpointer *pass_along )
3121 gchar *track_name = (gchar *) pass_along[3];
3122 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3124 gchar *escaped = uri_escape ( tr->comment );
3125 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3126 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3132 /* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */
3133 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3135 static GtkTreeIter staticiter;
3136 static gpointer pass_along[5];
3138 gboolean rv = FALSE;
3141 pass_along[1] = vlp;
3142 pass_along[2] = GINT_TO_POINTER (subtype);
3143 pass_along[3] = sublayer;
3144 staticiter = *iter; /* will exist after function has ended */
3145 pass_along[4] = &staticiter;
3147 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3151 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3153 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3154 gtk_widget_show ( item );
3156 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3157 VikTrwLayer *vtl = l;
3158 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3159 if (tr && tr->property_dialog)
3160 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3163 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3165 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3166 gtk_widget_show ( item );
3168 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3169 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3170 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3171 gtk_widget_show ( item );
3173 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3175 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3176 gtk_widget_show ( item );
3178 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3180 gboolean separator_created = FALSE;
3182 /* could be a right-click using the tool */
3183 if ( vlp != NULL ) {
3184 item = gtk_menu_item_new ();
3185 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3186 gtk_widget_show ( item );
3188 separator_created = TRUE;
3190 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3191 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3192 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3193 gtk_widget_show ( item );
3196 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3198 if ( !separator_created ) {
3199 item = gtk_menu_item_new ();
3200 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3201 gtk_widget_show ( item );
3204 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3205 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3206 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3207 gtk_widget_show ( item );
3213 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3215 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
3216 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3217 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3218 gtk_widget_show ( item );
3221 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3223 GtkWidget *goto_submenu;
3224 item = gtk_menu_item_new ();
3225 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3226 gtk_widget_show ( item );
3228 goto_submenu = gtk_menu_new ();
3229 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3230 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3231 gtk_widget_show ( item );
3232 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3234 item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
3235 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3236 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3237 gtk_widget_show ( item );
3239 item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3240 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3241 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3242 gtk_widget_show ( item );
3244 item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
3245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3246 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3247 gtk_widget_show ( item );
3249 item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3250 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3251 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3252 gtk_widget_show ( item );
3254 item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3256 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3257 gtk_widget_show ( item );
3259 item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3261 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3262 gtk_widget_show ( item );
3264 item = gtk_menu_item_new_with_mnemonic ( _("_View Track") );
3265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3266 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3267 gtk_widget_show ( item );
3269 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time") );
3270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3271 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3272 gtk_widget_show ( item );
3274 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3275 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3276 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3277 gtk_widget_show ( item );
3279 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time") );
3280 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3281 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3282 gtk_widget_show ( item );
3284 item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3285 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3286 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3287 gtk_widget_show ( item );
3289 item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3291 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3292 gtk_widget_show ( item );
3294 item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX") );
3295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3296 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3297 gtk_widget_show ( item );
3299 item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3300 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3301 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3302 gtk_widget_show ( item );
3304 item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Magic Scissors") );
3305 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
3306 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3307 gtk_widget_show ( item );
3309 #ifdef VIK_CONFIG_OPENSTREETMAP
3310 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
3311 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3312 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3313 gtk_widget_show ( item );
3316 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3318 item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3319 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3320 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3321 gtk_widget_show ( item );
3324 item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
3325 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3326 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3327 gtk_widget_show ( item );
3329 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3330 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3331 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3333 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3334 gtk_widget_show ( item );
3341 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3344 if (!vtl->current_tpl)
3346 if (!vtl->current_tpl->next)
3349 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3350 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3352 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3355 VikTrackpoint *tp_new = vik_trackpoint_new();
3356 struct LatLon ll_current, ll_next;
3357 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3358 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3360 /* main positional interpolation */
3361 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3362 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3364 /* Now other properties that can be interpolated */
3365 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3367 if (tp_current->has_timestamp && tp_next->has_timestamp) {
3368 /* Note here the division is applied to each part, then added
3369 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3370 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3371 tp_new->has_timestamp = TRUE;
3374 if (tp_current->speed != NAN && tp_next->speed != NAN)
3375 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3377 /* TODO - improve interpolation of course, as it may not be correct.
3378 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3379 [similar applies if value is in radians] */
3380 if (tp_current->course != NAN && tp_next->course != NAN)
3381 tp_new->speed = (tp_current->course + tp_next->course)/2;
3383 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3385 /* Insert new point into the trackpoints list after the current TP */
3386 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3387 gint index = g_list_index ( tr->trackpoints, tp_current );
3389 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3394 /* to be called when last_tpl no long exists. */
3395 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3397 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3398 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3399 vtl->last_tpl = NULL;
3400 vtl->last_tp_track_name = NULL;
3403 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3409 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3413 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3415 if ( vtl->current_tpl )
3417 vtl->current_tpl = NULL;
3418 vtl->current_tp_track_name = NULL;
3419 vik_layer_emit_update(VIK_LAYER(vtl));
3423 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3425 g_assert ( vtl->tpwin != NULL );
3426 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3427 trw_layer_cancel_current_tp ( vtl, TRUE );
3428 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3431 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, NULL ) ) )
3433 VikTrack *tr = vik_track_new ();
3434 GList *newglist = g_list_alloc ();
3435 newglist->prev = NULL;
3436 newglist->next = vtl->current_tpl->next;
3437 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3438 tr->trackpoints = newglist;
3440 vtl->current_tpl->next->prev = newglist; /* end old track here */
3441 vtl->current_tpl->next = NULL;
3443 vtl->current_tpl = newglist; /* change tp to first of new track. */
3444 vtl->current_tp_track_name = name;
3446 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3450 vik_trw_layer_add_track ( vtl, name, tr );
3451 vik_layer_emit_update(VIK_LAYER(vtl));
3454 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3456 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3458 g_assert(tr != NULL);
3460 /* can't join with a non-existent trackpoint */
3461 vtl->last_tpl = NULL;
3462 vtl->last_tp_track_name = NULL;
3464 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3466 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3467 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3469 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3471 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3472 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3474 trw_layer_cancel_last_tp ( vtl );
3476 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3477 g_list_free_1 ( vtl->current_tpl );
3478 vtl->current_tpl = new_tpl;
3479 vik_layer_emit_update(VIK_LAYER(vtl));
3483 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3484 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3485 g_list_free_1 ( vtl->current_tpl );
3486 trw_layer_cancel_current_tp ( vtl, FALSE );
3489 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3491 vtl->last_tpl = vtl->current_tpl;
3492 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3493 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3495 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3497 vtl->last_tpl = vtl->current_tpl;
3498 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3499 vik_layer_emit_update(VIK_LAYER(vtl));
3501 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
3503 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
3504 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3506 VikTrack *tr_first = tr1, *tr_last = tr2;
3510 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
3511 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
3512 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
3513 vik_track_reverse ( tr1 );
3514 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
3519 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
3521 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. */
3522 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
3523 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
3524 tr2->trackpoints = NULL;
3526 tmp = vtl->current_tp_track_name;
3528 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
3529 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3531 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
3532 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
3533 vik_trw_layer_delete_track ( vtl, tmp );
3535 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
3536 vik_layer_emit_update(VIK_LAYER(vtl));
3538 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
3540 trw_layer_insert_tp_after_current_tp ( vtl );
3541 vik_layer_emit_update(VIK_LAYER(vtl));
3543 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
3544 vik_layer_emit_update (VIK_LAYER(vtl));
3547 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
3551 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
3552 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
3553 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
3554 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
3555 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
3557 if ( vtl->current_tpl )
3558 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3559 /* set layer name and TP data */
3562 /***************************************************************************
3564 ***************************************************************************/
3566 /*** Utility data structures and functions ****/
3570 gint closest_x, closest_y;
3571 gchar *closest_wp_name;
3572 VikWaypoint *closest_wp;
3578 gint closest_x, closest_y;
3579 gchar *closest_track_name;
3580 VikTrackpoint *closest_tp;
3585 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
3591 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
3593 if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
3594 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
3595 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3597 params->closest_wp_name = name;
3598 params->closest_wp = wp;
3599 params->closest_x = x;
3600 params->closest_y = y;
3604 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
3606 GList *tpl = t->trackpoints;
3615 tp = VIK_TRACKPOINT(tpl->data);
3617 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
3619 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
3620 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
3621 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3623 params->closest_track_name = name;
3624 params->closest_tp = tp;
3625 params->closest_tpl = tpl;
3626 params->closest_x = x;
3627 params->closest_y = y;
3633 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3635 TPSearchParams params;
3639 params.closest_track_name = NULL;
3640 params.closest_tp = NULL;
3641 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
3642 return params.closest_tp;
3645 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3647 WPSearchParams params;
3651 params.closest_wp = NULL;
3652 params.closest_wp_name = NULL;
3653 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3654 return params.closest_wp;
3657 /* background drawing hook, to be passed the viewport */
3658 static gboolean tool_sync_done = TRUE;
3660 static gboolean tool_sync(gpointer data)
3662 VikViewport *vvp = data;
3663 gdk_threads_enter();
3664 vik_viewport_sync(vvp);
3665 tool_sync_done = TRUE;
3666 gdk_threads_leave();
3677 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
3680 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
3681 gdk_gc_set_function ( t->gc, GDK_INVERT );
3682 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3683 vik_viewport_sync(t->vvp);
3688 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
3690 VikViewport *vvp = t->vvp;
3691 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3692 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3696 if (tool_sync_done) {
3697 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
3698 tool_sync_done = FALSE;
3702 static void marker_end_move ( tool_ed_t *t )
3704 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3705 g_object_unref ( t->gc );
3709 /*** Edit waypoint ****/
3711 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3713 tool_ed_t *t = g_new(tool_ed_t, 1);
3719 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3721 WPSearchParams params;
3722 tool_ed_t *t = data;
3723 VikViewport *vvp = t->vvp;
3725 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3732 if ( !vtl->vl.visible || !vtl->waypoints_visible )
3735 if ( vtl->current_wp && vtl->current_wp->visible )
3737 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
3739 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
3741 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
3742 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
3744 if ( event->button == 3 )
3745 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3747 marker_begin_move(t, event->x, event->y);
3754 params.x = event->x;
3755 params.y = event->y;
3756 params.closest_wp_name = NULL;
3757 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3758 params.closest_wp = NULL;
3759 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
3760 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
3762 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
3763 marker_begin_move(t, event->x, event->y);
3764 g_critical("shouldn't be here");
3767 else if ( params.closest_wp )
3769 if ( event->button == 3 )
3770 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3772 vtl->waypoint_rightclick = FALSE;
3774 vtl->current_wp = params.closest_wp;
3775 vtl->current_wp_name = params.closest_wp_name;
3777 if ( params.closest_wp )
3778 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
3780 /* could make it so don't update if old WP is off screen and new is null but oh well */
3781 vik_layer_emit_update ( VIK_LAYER(vtl) );
3785 vtl->current_wp = NULL;
3786 vtl->current_wp_name = NULL;
3787 vtl->waypoint_rightclick = FALSE;
3788 vik_layer_emit_update ( VIK_LAYER(vtl) );
3792 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
3794 tool_ed_t *t = data;
3795 VikViewport *vvp = t->vvp;
3797 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3802 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3805 if ( event->state & GDK_CONTROL_MASK )
3807 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3809 new_coord = tp->coord;
3813 if ( event->state & GDK_SHIFT_MASK )
3815 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3816 if ( wp && wp != vtl->current_wp )
3817 new_coord = wp->coord;
3822 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3824 marker_moveto ( t, x, y );
3831 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3833 tool_ed_t *t = data;
3834 VikViewport *vvp = t->vvp;
3836 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3839 if ( t->holding && event->button == 1 )
3842 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3845 if ( event->state & GDK_CONTROL_MASK )
3847 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3849 new_coord = tp->coord;
3853 if ( event->state & GDK_SHIFT_MASK )
3855 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3856 if ( wp && wp != vtl->current_wp )
3857 new_coord = wp->coord;
3860 marker_end_move ( t );
3862 vtl->current_wp->coord = new_coord;
3863 vik_layer_emit_update ( VIK_LAYER(vtl) );
3866 /* PUT IN RIGHT PLACE!!! */
3867 if ( event->button == 3 && vtl->waypoint_rightclick )
3869 if ( vtl->wp_right_click_menu )
3870 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
3871 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
3872 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 ) );
3873 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
3874 vtl->waypoint_rightclick = FALSE;
3879 /**** Begin track ***/
3880 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
3885 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3887 vtl->current_track = NULL;
3888 return tool_new_track_click ( vtl, event, vvp );
3891 /*** New track ****/
3893 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
3902 } new_track_move_passalong_t;
3904 /* sync and undraw, but only when we have time */
3905 static gboolean ct_sync ( gpointer passalong )
3907 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
3908 vik_viewport_sync ( p->vvp );
3909 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
3910 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
3911 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
3912 p->vtl->ct_sync_done = TRUE;
3917 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
3919 /* if we haven't sync'ed yet, we don't have time to do more. */
3920 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
3921 GList *iter = vtl->current_track->trackpoints;
3922 new_track_move_passalong_t *passalong;
3925 while ( iter->next )
3927 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
3928 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
3929 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
3930 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
3932 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
3933 passalong->vtl = vtl;
3934 passalong->vvp = vvp;
3937 passalong->x2 = event->x;
3938 passalong->y2 = event->y;
3940 /* this will sync and undraw when we have time to */
3941 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
3942 vtl->ct_sync_done = FALSE;
3943 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
3945 return VIK_LAYER_TOOL_ACK;
3948 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
3950 if ( vtl->current_track && event->keyval == GDK_Escape ) {
3951 vtl->current_track = NULL;
3952 vik_layer_emit_update ( VIK_LAYER(vtl) );
3954 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
3956 if ( vtl->current_track->trackpoints )
3958 GList *last = g_list_last(vtl->current_track->trackpoints);
3959 g_free ( last->data );
3960 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3962 vik_layer_emit_update ( VIK_LAYER(vtl) );
3968 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
3972 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3975 if ( event->button == 3 && vtl->current_track )
3978 if ( vtl->current_track->trackpoints )
3980 GList *last = g_list_last(vtl->current_track->trackpoints);
3981 g_free ( last->data );
3982 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3984 vik_layer_emit_update ( VIK_LAYER(vtl) );
3988 if ( event->type == GDK_2BUTTON_PRESS )
3990 /* subtract last (duplicate from double click) tp then end */
3991 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
3993 GList *last = g_list_last(vtl->current_track->trackpoints);
3994 g_free ( last->data );
3995 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
3996 /* undo last, then end */
3997 vtl->current_track = NULL;
3999 vik_layer_emit_update ( VIK_LAYER(vtl) );
4003 if ( ! vtl->current_track )
4005 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
4006 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
4008 vtl->current_track = vik_track_new();
4009 vtl->current_track->visible = TRUE;
4010 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
4012 /* incase it was created by begin track */
4013 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
4018 tp = vik_trackpoint_new();
4019 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
4021 /* snap to other TP */
4022 if ( event->state & GDK_CONTROL_MASK )
4024 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4026 tp->coord = other_tp->coord;
4029 tp->newsegment = FALSE;
4030 tp->has_timestamp = FALSE;
4032 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
4034 vtl->ct_x1 = vtl->ct_x2;
4035 vtl->ct_y1 = vtl->ct_y2;
4036 vtl->ct_x2 = event->x;
4037 vtl->ct_y2 = event->y;
4039 vik_layer_emit_update ( VIK_LAYER(vtl) );
4044 /*** New waypoint ****/
4046 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4051 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4054 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4056 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
4057 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
4058 vik_layer_emit_update ( VIK_LAYER(vtl) );
4063 /*** Edit trackpoint ****/
4065 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
4067 tool_ed_t *t = g_new(tool_ed_t, 1);
4073 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4075 tool_ed_t *t = data;
4076 VikViewport *vvp = t->vvp;
4077 TPSearchParams params;
4078 /* OUTDATED DOCUMENTATION:
4079 find 5 pixel range on each side. then put these UTM, and a pointer
4080 to the winning track name (and maybe the winning track itself), and a
4081 pointer to the winning trackpoint, inside an array or struct. pass
4082 this along, do a foreach on the tracks which will do a foreach on the
4085 params.x = event->x;
4086 params.y = event->y;
4087 params.closest_track_name = NULL;
4088 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4089 params.closest_tp = NULL;
4091 if ( event->button != 1 )
4094 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4097 if ( !vtl->vl.visible || !vtl->tracks_visible )
4100 if ( vtl->current_tpl )
4102 /* 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.) */
4103 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
4104 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
4106 g_assert ( current_tr );
4108 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
4110 if ( current_tr->visible &&
4111 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
4112 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
4113 marker_begin_move ( t, event->x, event->y );
4117 vtl->last_tpl = vtl->current_tpl;
4118 vtl->last_tp_track_name = vtl->current_tp_track_name;
4121 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
4123 if ( params.closest_tp )
4125 vtl->current_tpl = params.closest_tpl;
4126 vtl->current_tp_track_name = params.closest_track_name;
4127 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
4128 trw_layer_tpwin_init ( vtl );
4129 vik_layer_emit_update ( VIK_LAYER(vtl) );
4133 /* these aren't the droids you're looking for */
4137 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4139 tool_ed_t *t = data;
4140 VikViewport *vvp = t->vvp;
4142 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4148 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4151 if ( event->state & GDK_CONTROL_MASK )
4153 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4154 if ( tp && tp != vtl->current_tpl->data )
4155 new_coord = tp->coord;
4157 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4160 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4161 marker_moveto ( t, x, y );
4169 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4171 tool_ed_t *t = data;
4172 VikViewport *vvp = t->vvp;
4174 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4176 if ( event->button != 1)
4181 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4184 if ( event->state & GDK_CONTROL_MASK )
4186 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4187 if ( tp && tp != vtl->current_tpl->data )
4188 new_coord = tp->coord;
4191 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4193 marker_end_move ( t );
4195 /* diff dist is diff from orig */
4196 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4197 /* can't join with itself! */
4198 trw_layer_cancel_last_tp ( vtl );
4200 vik_layer_emit_update ( VIK_LAYER(vtl) );
4207 /*** Magic Scissors ***/
4208 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
4213 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4216 if ( !vtl ) return FALSE;
4217 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
4218 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
4220 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
4222 vtl->magic_scissors_coord = *new_end;
4224 vik_layer_emit_update ( VIK_LAYER(vtl) );
4225 /* remove last ' to:...' */
4226 if ( vtl->magic_scissors_current_track->comment ) {
4227 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
4228 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
4229 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
4230 last_to - vtl->magic_scissors_current_track->comment - 1);
4231 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4236 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
4237 struct LatLon start, end;
4238 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
4239 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
4242 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
4243 vik_coord_to_latlon ( &(tmp), &end );
4244 vtl->magic_scissors_coord = tmp; /* for continuations */
4246 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
4247 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
4248 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
4250 vtl->magic_scissors_check_added_track = TRUE;
4251 vtl->magic_scissors_started = FALSE;
4254 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
4255 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
4256 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
4257 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
4258 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
4259 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
4262 /* see if anything was done -- a track was added or appended to */
4263 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
4266 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
4269 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
4271 vtl->magic_scissors_current_track = tr;
4273 g_free ( vtl->magic_scissors_added_track_name );
4274 vtl->magic_scissors_added_track_name = NULL;
4275 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
4276 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
4277 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
4278 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4280 vtl->magic_scissors_check_added_track = FALSE;
4281 vtl->magic_scissors_append = FALSE;
4283 vik_layer_emit_update ( VIK_LAYER(vtl) );
4285 vtl->magic_scissors_started = TRUE;
4286 vtl->magic_scissors_coord = tmp;
4287 vtl->magic_scissors_current_track = NULL;
4292 /*** Show picture ****/
4294 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
4299 /* Params are: vvp, event, last match found or NULL */
4300 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
4302 if ( wp->image && wp->visible )
4304 gint x, y, slackx, slacky;
4305 GdkEventButton *event = (GdkEventButton *) params[1];
4307 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
4308 slackx = wp->image_width / 2;
4309 slacky = wp->image_height / 2;
4310 if ( x <= event->x + slackx && x >= event->x - slackx
4311 && y <= event->y + slacky && y >= event->y - slacky )
4313 params[2] = wp->image; /* we've found a match. however continue searching
4314 * since we want to find the last match -- that
4315 * is, the match that was drawn last. */
4320 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4322 gpointer params[3] = { vvp, event, NULL };
4323 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4325 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
4328 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
4330 ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
4333 gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
4334 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
4335 g_free ( quoted_file );
4336 if ( ! g_spawn_command_line_async ( cmd, &err ) )
4338 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch eog to open file.") );
4339 g_error_free ( err );
4342 #endif /* WINDOWS */
4343 return TRUE; /* found a match */
4346 return FALSE; /* go through other layers, searching for a match */
4349 /***************************************************************************
4351 ***************************************************************************/
4357 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
4359 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
4360 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
4363 static void create_thumbnails_thread ( GSList *pics, gpointer threaddata )
4365 guint total = g_slist_length(pics), done = 0;
4368 a_thumbnails_create ( (gchar *) pics->data );
4369 a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
4374 static void free_pics_slist ( GSList *pics )
4378 g_free ( pics->data );
4379 pics = g_slist_delete_link ( pics, pics );
4383 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
4385 if ( ! vtl->has_verified_thumbnails )
4387 GSList *pics = NULL;
4388 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
4391 gint len = g_slist_length ( pics );
4392 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
4393 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 );
4399 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
4401 return vtl->coord_mode;
4406 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
4408 vik_coord_convert ( &(wp->coord), *dest_mode );
4411 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
4413 vik_track_convert ( tr, *dest_mode );
4416 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
4418 if ( vtl->coord_mode != dest_mode )
4420 vtl->coord_mode = dest_mode;
4421 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
4422 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
4426 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
4428 return g_hash_table_lookup ( vtl->waypoints, name );
4431 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
4433 return g_hash_table_lookup ( vtl->tracks, name );
4436 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
4438 vtl->menu_selection = selection;
4441 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
4443 return(vtl->menu_selection);
4446 /* ----------- Downloading maps along tracks --------------- */
4448 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
4450 /* TODO: calculating based on current size of viewport */
4451 const gdouble w_at_zoom_0_125 = 0.0013;
4452 const gdouble h_at_zoom_0_125 = 0.0011;
4453 gdouble zoom_factor = zoom_level/0.125;
4455 wh->lat = h_at_zoom_0_125 * zoom_factor;
4456 wh->lon = w_at_zoom_0_125 * zoom_factor;
4458 return 0; /* all OK */
4461 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
4463 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
4464 (dist->lat >= ABS(to->north_south - from->north_south)))
4467 VikCoord *coord = g_malloc(sizeof(VikCoord));
4468 coord->mode = VIK_COORD_LATLON;
4470 if (ABS(gradient) < 1) {
4471 if (from->east_west > to->east_west)
4472 coord->east_west = from->east_west - dist->lon;
4474 coord->east_west = from->east_west + dist->lon;
4475 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
4477 if (from->north_south > to->north_south)
4478 coord->north_south = from->north_south - dist->lat;
4480 coord->north_south = from->north_south + dist->lat;
4481 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
4487 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
4489 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
4490 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
4492 VikCoord *next = from;
4494 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
4496 list = g_list_prepend(list, next);
4502 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
4504 typedef struct _Rect {
4509 #define GLRECT(iter) ((Rect *)((iter)->data))
4512 GList *rects_to_download = NULL;
4515 if (get_download_area_width(vvp, zoom_level, &wh))
4518 GList *iter = tr->trackpoints;
4522 gboolean new_map = TRUE;
4523 VikCoord *cur_coord, tl, br;
4526 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
4528 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4529 rect = g_malloc(sizeof(Rect));
4532 rect->center = *cur_coord;
4533 rects_to_download = g_list_prepend(rects_to_download, rect);
4538 gboolean found = FALSE;
4539 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4540 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
4551 GList *fillins = NULL;
4552 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
4553 /* seems that ATM the function get_next_coord works only for LATLON */
4554 if ( cur_coord->mode == VIK_COORD_LATLON ) {
4555 /* fill-ins for far apart points */
4556 GList *cur_rect, *next_rect;
4557 for (cur_rect = rects_to_download;
4558 (next_rect = cur_rect->next) != NULL;
4559 cur_rect = cur_rect->next) {
4560 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
4561 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
4562 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
4566 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
4569 GList *iter = fillins;
4571 cur_coord = (VikCoord *)(iter->data);
4572 vik_coord_set_area(cur_coord, &wh, &tl, &br);
4573 rect = g_malloc(sizeof(Rect));
4576 rect->center = *cur_coord;
4577 rects_to_download = g_list_prepend(rects_to_download, rect);
4582 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4583 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
4587 for (iter = fillins; iter; iter = iter->next)
4589 g_list_free(fillins);
4591 if (rects_to_download) {
4592 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
4593 g_free(rect_iter->data);
4594 g_list_free(rects_to_download);
4598 static void trw_layer_download_map_along_track_cb(gpointer pass_along[6])
4601 gint selected_map, default_map;
4602 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
4603 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
4604 gint selected_zoom, default_zoom;
4608 VikTrwLayer *vtl = pass_along[0];
4609 VikLayersPanel *vlp = pass_along[1];
4610 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4611 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
4613 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS);
4614 int num_maps = g_list_length(vmls);
4617 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
4621 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
4622 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
4624 gchar **np = map_names;
4625 VikMapsLayer **lp = map_layers;
4626 for (i = 0; i < num_maps; i++) {
4627 gboolean dup = FALSE;
4628 vml = (VikMapsLayer *)(vmls->data);
4629 for (j = 0; j < i; j++) { /* no duplicate allowed */
4630 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
4637 *np++ = vik_maps_layer_get_map_label(vml);
4643 num_maps = lp - map_layers;
4645 for (default_map = 0; default_map < num_maps; default_map++) {
4646 /* TODO: check for parent layer's visibility */
4647 if (VIK_LAYER(map_layers[default_map])->visible)
4650 default_map = (default_map == num_maps) ? 0 : default_map;
4652 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
4653 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
4654 if (cur_zoom == zoom_vals[default_zoom])
4657 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
4659 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
4662 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
4665 for (i = 0; i < num_maps; i++)
4666 g_free(map_names[i]);
4674 /**** lowest waypoint number calculation ***/
4675 static gint highest_wp_number_name_to_number(const gchar *name) {
4676 if ( strlen(name) == 3 ) {
4678 if ( n < 100 && name[0] != '0' )
4680 if ( n < 10 && name[0] != '0' )
4688 static void highest_wp_number_reset(VikTrwLayer *vtl)
4690 vtl->highest_wp_number = -1;
4693 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
4695 /* if is bigger that top, add it */
4696 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
4697 if ( new_wp_num > vtl->highest_wp_number )
4698 vtl->highest_wp_number = new_wp_num;
4701 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
4703 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
4704 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
4705 if ( vtl->highest_wp_number == old_wp_num ) {
4707 vtl->highest_wp_number --;
4709 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4710 /* search down until we find something that *does* exist */
4712 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
4713 vtl->highest_wp_number --;
4714 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4719 /* get lowest unused number */
4720 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
4723 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
4725 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
4726 return g_strdup(buf);